Use python.h from libreplace
[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 "lib/replace/system/python.h"
27 #include "includes.h"
28 #include "python/py3compat.h"
29 #include "python/modules.h"
30 #include "smbd/smbd.h"
31 #include "libcli/util/pyerrors.h"
32 #include "librpc/rpc/pyrpc_util.h"
33 #include <pytalloc.h>
34 #include "system/filesys.h"
35 #include "passdb.h"
36 #include "secrets.h"
37 #include "auth.h"
38
39 extern const struct generic_mapping file_generic_mapping;
40
41 #undef  DBGC_CLASS
42 #define DBGC_CLASS DBGC_ACLS
43
44 #ifdef O_DIRECTORY
45 #define DIRECTORY_FLAGS O_RDONLY|O_DIRECTORY
46 #else
47 /* POSIX allows us to open a directory with O_RDONLY. */
48 #define DIRECTORY_FLAGS O_RDONLY
49 #endif
50
51
52 static connection_struct *get_conn_tos(
53         const char *service,
54         const struct auth_session_info *session_info)
55 {
56         struct conn_struct_tos *c = NULL;
57         int snum = -1;
58         NTSTATUS status;
59         char *cwd = NULL;
60         struct smb_filename cwd_fname = {0};
61         int ret;
62
63         if (!posix_locking_init(false)) {
64                 PyErr_NoMemory();
65                 return NULL;
66         }
67
68         if (service) {
69                 snum = lp_servicenumber(service);
70                 if (snum == -1) {
71                         PyErr_SetString(PyExc_RuntimeError, "unknown service");
72                         return NULL;
73                 }
74         }
75
76         /*
77          * Make sure that session unix info is filled,
78          * which is required by vfs operations.
79          */
80         if (session_info->unix_info == NULL) {
81                 PyErr_SetString(PyExc_RuntimeError,
82                                 "Session unix info not initialized");
83                 return NULL;
84         }
85         if (session_info->unix_info->unix_name == NULL) {
86                 PyErr_SetString(PyExc_RuntimeError,
87                                 "Session unix info not available");
88                 return NULL;
89         }
90
91         status = create_conn_struct_tos(NULL,
92                                         snum,
93                                         "/",
94                                         session_info,
95                                         &c);
96         PyErr_NTSTATUS_IS_ERR_RAISE(status);
97
98         /* Ignore read-only and share restrictions */
99         c->conn->read_only = false;
100         c->conn->share_access = SEC_RIGHTS_FILE_ALL;
101
102         /* Provided by libreplace if not present. Always mallocs. */
103         cwd = get_current_dir_name();
104         if (cwd == NULL) {
105                 PyErr_NoMemory();
106                 return NULL;
107         }
108
109         cwd_fname.base_name = cwd;
110         /*
111          * We need to call vfs_ChDir() to initialize
112          * conn->cwd_fsp correctly. Change directory
113          * to current directory (so no change for process).
114          */
115         ret = vfs_ChDir(c->conn, &cwd_fname);
116         if (ret != 0) {
117                 status = map_nt_error_from_unix(errno);
118                 SAFE_FREE(cwd);
119                 PyErr_NTSTATUS_IS_ERR_RAISE(status);
120         }
121
122         SAFE_FREE(cwd);
123
124         return c->conn;
125 }
126
127 static int set_sys_acl_conn(const char *fname,
128                                  SMB_ACL_TYPE_T acltype,
129                                  SMB_ACL_T theacl, connection_struct *conn)
130 {
131         int ret;
132         struct smb_filename *smb_fname = NULL;
133         TALLOC_CTX *frame = talloc_stackframe();
134         NTSTATUS status;
135
136         smb_fname = synthetic_smb_fname_split(frame,
137                                         fname,
138                                         lp_posix_pathnames());
139         if (smb_fname == NULL) {
140                 TALLOC_FREE(frame);
141                 return -1;
142         }
143
144         status = openat_pathref_fsp(conn->cwd_fsp, smb_fname);
145         if (!NT_STATUS_IS_OK(status)) {
146                 TALLOC_FREE(frame);
147                 errno = map_errno_from_nt_status(status);
148                 return -1;
149         }
150
151         ret = SMB_VFS_SYS_ACL_SET_FD(smb_fname->fsp, acltype, theacl);
152
153         status = fd_close(smb_fname->fsp);
154         if (!NT_STATUS_IS_OK(status)) {
155                 TALLOC_FREE(frame);
156                 errno = map_errno_from_nt_status(status);
157                 return -1;
158         }
159
160         TALLOC_FREE(frame);
161         return ret;
162 }
163
164
165 static NTSTATUS init_files_struct(TALLOC_CTX *mem_ctx,
166                                   const char *fname,
167                                   struct connection_struct *conn,
168                                   int flags,
169                                   struct files_struct **_fsp)
170 {
171         struct vfs_open_how how = { .flags = flags, .mode = 0644 };
172         struct smb_filename *smb_fname = NULL;
173         int fd;
174         mode_t saved_umask;
175         struct files_struct *fsp;
176         struct files_struct *fspcwd = NULL;
177         NTSTATUS status;
178
179         fsp = talloc_zero(mem_ctx, struct files_struct);
180         if (fsp == NULL) {
181                 return NT_STATUS_NO_MEMORY;
182         }
183         fsp->fh = fd_handle_create(fsp);
184         if (fsp->fh == NULL) {
185                 return NT_STATUS_NO_MEMORY;
186         }
187         fsp->conn = conn;
188
189         smb_fname = synthetic_smb_fname_split(fsp,
190                                               fname,
191                                               lp_posix_pathnames());
192         if (smb_fname == NULL) {
193                 return NT_STATUS_NO_MEMORY;
194         }
195
196         fsp->fsp_name = smb_fname;
197
198         status = vfs_at_fspcwd(fsp, conn, &fspcwd);
199         if (!NT_STATUS_IS_OK(status)) {
200                 return status;
201         }
202
203         /*
204          * we want total control over the permissions on created files,
205          * so set our umask to 0 (this matters if flags contains O_CREAT)
206          */
207         saved_umask = umask(0);
208
209         fd = SMB_VFS_OPENAT(conn,
210                             fspcwd,
211                             smb_fname,
212                             fsp,
213                             &how);
214
215         umask(saved_umask);
216
217         if (fd == -1) {
218                 int err = errno;
219                 if (err == ENOENT) {
220                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
221                 }
222                 return NT_STATUS_INVALID_PARAMETER;
223         }
224         fsp_set_fd(fsp, fd);
225
226         status = vfs_stat_fsp(fsp);
227         if (!NT_STATUS_IS_OK(status)) {
228                 /* If we have an fd, this stat should succeed. */
229                 DEBUG(0,("Error doing fstat on open file %s (%s)\n",
230                          smb_fname_str_dbg(smb_fname),
231                          nt_errstr(status) ));
232                 return status;
233         }
234
235         fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
236         fsp->vuid = UID_FIELD_INVALID;
237         fsp->file_pid = 0;
238         fsp->fsp_flags.can_lock = true;
239         fsp->fsp_flags.can_read = true;
240         fsp->fsp_flags.can_write = true;
241         fsp->print_file = NULL;
242         fsp->fsp_flags.modified = false;
243         fsp->sent_oplock_break = NO_BREAK_SENT;
244         fsp->fsp_flags.is_directory = S_ISDIR(smb_fname->st.st_ex_mode);
245
246         *_fsp = fsp;
247
248         return NT_STATUS_OK;
249 }
250
251 static NTSTATUS set_nt_acl_conn(const char *fname,
252                                 uint32_t security_info_sent, const struct security_descriptor *sd,
253                                 connection_struct *conn)
254 {
255         TALLOC_CTX *frame = talloc_stackframe();
256         struct files_struct *fsp = NULL;
257         NTSTATUS status = NT_STATUS_OK;
258
259         /* first, try to open it as a file with flag O_RDWR */
260         status = init_files_struct(frame,
261                                    fname,
262                                    conn,
263                                    O_RDWR,
264                                    &fsp);
265         if (!NT_STATUS_IS_OK(status) && errno == EISDIR) {
266                 /* if fail, try to open as dir */
267                 status = init_files_struct(frame,
268                                            fname,
269                                            conn,
270                                            DIRECTORY_FLAGS,
271                                            &fsp);
272         }
273
274         if (!NT_STATUS_IS_OK(status)) {
275                 DBG_ERR("init_files_struct failed: %s\n",
276                         nt_errstr(status));
277                 if (fsp != NULL) {
278                         fd_close(fsp);
279                 }
280                 TALLOC_FREE(frame);
281                 return status;
282         }
283
284         status = SMB_VFS_FSET_NT_ACL(metadata_fsp(fsp), security_info_sent, sd);
285         if (!NT_STATUS_IS_OK(status)) {
286                 DEBUG(0,("set_nt_acl_conn: fset_nt_acl returned %s.\n", nt_errstr(status)));
287         }
288
289         fd_close(fsp);
290
291         TALLOC_FREE(frame);
292         return status;
293 }
294
295 static NTSTATUS get_nt_acl_conn(TALLOC_CTX *mem_ctx,
296                                 const char *fname,
297                                 connection_struct *conn,
298                                 uint32_t security_info_wanted,
299                                 struct security_descriptor **sd)
300 {
301         TALLOC_CTX *frame = talloc_stackframe();
302         NTSTATUS status;
303         struct smb_filename *smb_fname =  NULL;
304
305         smb_fname = synthetic_smb_fname_split(frame,
306                                         fname,
307                                         lp_posix_pathnames());
308
309         if (smb_fname == NULL) {
310                 TALLOC_FREE(frame);
311                 return NT_STATUS_NO_MEMORY;
312         }
313
314         status = openat_pathref_fsp(conn->cwd_fsp, smb_fname);
315         if (!NT_STATUS_IS_OK(status)) {
316                 TALLOC_FREE(frame);
317                 return status;
318         }
319
320         status = SMB_VFS_FGET_NT_ACL(metadata_fsp(smb_fname->fsp),
321                                 security_info_wanted,
322                                 mem_ctx,
323                                 sd);
324         if (!NT_STATUS_IS_OK(status)) {
325                 DBG_ERR("fget_nt_acl_at returned %s.\n",
326                         nt_errstr(status));
327         }
328
329         status = fd_close(smb_fname->fsp);
330         if (!NT_STATUS_IS_OK(status)) {
331                 TALLOC_FREE(frame);
332                 return status;
333         }
334
335         TALLOC_FREE(frame);
336
337         return status;
338 }
339
340 static int set_acl_entry_perms(SMB_ACL_ENTRY_T entry, mode_t perm_mask)
341 {
342         SMB_ACL_PERMSET_T perms = NULL;
343
344         if (sys_acl_get_permset(entry, &perms) != 0) {
345                 return -1;
346         }
347
348         if (sys_acl_clear_perms(perms) != 0) {
349                 return -1;
350         }
351
352         if ((perm_mask & SMB_ACL_READ) != 0 &&
353             sys_acl_add_perm(perms, SMB_ACL_READ) != 0) {
354                 return -1;
355         }
356
357         if ((perm_mask & SMB_ACL_WRITE) != 0 &&
358             sys_acl_add_perm(perms, SMB_ACL_WRITE) != 0) {
359                 return -1;
360         }
361
362         if ((perm_mask & SMB_ACL_EXECUTE) != 0 &&
363             sys_acl_add_perm(perms, SMB_ACL_EXECUTE) != 0) {
364                 return -1;
365         }
366
367         if (sys_acl_set_permset(entry, perms) != 0) {
368                 return -1;
369         }
370
371         return 0;
372 }
373
374 static SMB_ACL_T make_simple_acl(TALLOC_CTX *mem_ctx,
375                         gid_t gid,
376                         mode_t chmod_mode)
377 {
378         mode_t mode = SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE;
379
380         mode_t mode_user = (chmod_mode & 0700) >> 6;
381         mode_t mode_group = (chmod_mode & 070) >> 3;
382         mode_t mode_other = chmod_mode &  07;
383         SMB_ACL_ENTRY_T entry;
384         SMB_ACL_T acl = sys_acl_init(mem_ctx);
385
386         if (!acl) {
387                 return NULL;
388         }
389
390         if (sys_acl_create_entry(&acl, &entry) != 0) {
391                 TALLOC_FREE(acl);
392                 return NULL;
393         }
394
395         if (sys_acl_set_tag_type(entry, SMB_ACL_USER_OBJ) != 0) {
396                 TALLOC_FREE(acl);
397                 return NULL;
398         }
399
400         if (set_acl_entry_perms(entry, mode_user) != 0) {
401                 TALLOC_FREE(acl);
402                 return NULL;
403         }
404
405         if (sys_acl_create_entry(&acl, &entry) != 0) {
406                 TALLOC_FREE(acl);
407                 return NULL;
408         }
409
410         if (sys_acl_set_tag_type(entry, SMB_ACL_GROUP_OBJ) != 0) {
411                 TALLOC_FREE(acl);
412                 return NULL;
413         }
414
415         if (set_acl_entry_perms(entry, mode_group) != 0) {
416                 TALLOC_FREE(acl);
417                 return NULL;
418         }
419
420         if (sys_acl_create_entry(&acl, &entry) != 0) {
421                 TALLOC_FREE(acl);
422                 return NULL;
423         }
424
425         if (sys_acl_set_tag_type(entry, SMB_ACL_OTHER) != 0) {
426                 TALLOC_FREE(acl);
427                 return NULL;
428         }
429
430         if (set_acl_entry_perms(entry, mode_other) != 0) {
431                 TALLOC_FREE(acl);
432                 return NULL;
433         }
434
435         if (gid != -1) {
436                 if (sys_acl_create_entry(&acl, &entry) != 0) {
437                         TALLOC_FREE(acl);
438                         return NULL;
439                 }
440
441                 if (sys_acl_set_tag_type(entry, SMB_ACL_GROUP) != 0) {
442                         TALLOC_FREE(acl);
443                         return NULL;
444                 }
445
446                 if (sys_acl_set_qualifier(entry, &gid) != 0) {
447                         TALLOC_FREE(acl);
448                         return NULL;
449                 }
450
451                 if (set_acl_entry_perms(entry, mode_group) != 0) {
452                         TALLOC_FREE(acl);
453                         return NULL;
454                 }
455         }
456
457         if (sys_acl_create_entry(&acl, &entry) != 0) {
458                 TALLOC_FREE(acl);
459                 return NULL;
460         }
461
462         if (sys_acl_set_tag_type(entry, SMB_ACL_MASK) != 0) {
463                 TALLOC_FREE(acl);
464                 return NULL;
465         }
466
467         if (set_acl_entry_perms(entry, mode) != 0) {
468                 TALLOC_FREE(acl);
469                 return NULL;
470         }
471
472         return acl;
473 }
474
475 /*
476   set a simple ACL on a file, as a test
477  */
478 static PyObject *py_smbd_set_simple_acl(PyObject *self, PyObject *args, PyObject *kwargs)
479 {
480         const char * const kwnames[] = {
481                 "fname",
482                 "mode",
483                 "session_info",
484                 "gid",
485                 "service",
486                 NULL
487         };
488         char *fname, *service = NULL;
489         PyObject *py_session = Py_None;
490         struct auth_session_info *session_info = NULL;
491         int ret;
492         int mode, gid = -1;
493         SMB_ACL_T acl;
494         TALLOC_CTX *frame;
495         connection_struct *conn;
496
497         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO|iz",
498                                          discard_const_p(char *, kwnames),
499                                          &fname,
500                                          &mode,
501                                          &py_session,
502                                          &gid,
503                                          &service))
504                 return NULL;
505
506         if (!py_check_dcerpc_type(py_session,
507                                   "samba.dcerpc.auth",
508                                   "session_info")) {
509                 return NULL;
510         }
511         session_info = pytalloc_get_type(py_session,
512                                          struct auth_session_info);
513         if (session_info == NULL) {
514                 PyErr_Format(PyExc_TypeError,
515                              "Expected auth_session_info for session_info argument got %s",
516                              pytalloc_get_name(py_session));
517                 return NULL;
518         }
519
520         frame = talloc_stackframe();
521
522         acl = make_simple_acl(frame, gid, mode);
523         if (acl == NULL) {
524                 TALLOC_FREE(frame);
525                 return NULL;
526         }
527
528         conn = get_conn_tos(service, session_info);
529         if (!conn) {
530                 TALLOC_FREE(frame);
531                 return NULL;
532         }
533
534         ret = set_sys_acl_conn(fname, SMB_ACL_TYPE_ACCESS, acl, conn);
535
536         if (ret != 0) {
537                 TALLOC_FREE(frame);
538                 errno = ret;
539                 return PyErr_SetFromErrno(PyExc_OSError);
540         }
541
542         TALLOC_FREE(frame);
543
544         Py_RETURN_NONE;
545 }
546
547 /*
548   chown a file
549  */
550 static PyObject *py_smbd_chown(PyObject *self, PyObject *args, PyObject *kwargs)
551 {
552         const char * const kwnames[] = {
553                 "fname",
554                 "uid",
555                 "gid",
556                 "session_info",
557                 "service",
558                 NULL
559         };
560         connection_struct *conn;
561         int ret;
562         NTSTATUS status;
563         char *fname, *service = NULL;
564         PyObject *py_session = Py_None;
565         struct auth_session_info *session_info = NULL;
566         int uid, gid;
567         TALLOC_CTX *frame;
568         struct files_struct *fsp = NULL;
569
570         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siiO|z",
571                                          discard_const_p(char *, kwnames),
572                                          &fname,
573                                          &uid,
574                                          &gid,
575                                          &py_session,
576                                          &service))
577                 return NULL;
578
579         if (!py_check_dcerpc_type(py_session,
580                                   "samba.dcerpc.auth",
581                                   "session_info")) {
582                 return NULL;
583         }
584         session_info = pytalloc_get_type(py_session,
585                                          struct auth_session_info);
586         if (session_info == NULL) {
587                 PyErr_Format(PyExc_TypeError,
588                              "Expected auth_session_info for session_info argument got %s",
589                              pytalloc_get_name(py_session));
590                 return NULL;
591         }
592
593         frame = talloc_stackframe();
594
595         conn = get_conn_tos(service, session_info);
596         if (!conn) {
597                 TALLOC_FREE(frame);
598                 return NULL;
599         }
600
601         /* first, try to open it as a file with flag O_RDWR */
602         status = init_files_struct(frame,
603                                    fname,
604                                    conn,
605                                    O_RDWR,
606                                    &fsp);
607         if (!NT_STATUS_IS_OK(status) && errno == EISDIR) {
608                 /* if fail, try to open as dir */
609                 status = init_files_struct(frame,
610                                            fname,
611                                            conn,
612                                            DIRECTORY_FLAGS,
613                                            &fsp);
614         }
615
616         if (!NT_STATUS_IS_OK(status)) {
617                 DBG_ERR("init_files_struct failed: %s\n",
618                         nt_errstr(status));
619                 if (fsp != NULL) {
620                         fd_close(fsp);
621                 }
622                 TALLOC_FREE(frame);
623                 /*
624                  * The following macro raises a python
625                  * error then returns NULL.
626                  */
627                 PyErr_NTSTATUS_IS_ERR_RAISE(status);
628         }
629
630         ret = SMB_VFS_FCHOWN(fsp, uid, gid);
631         if (ret != 0) {
632                 int saved_errno = errno;
633                 fd_close(fsp);
634                 TALLOC_FREE(frame);
635                 errno = saved_errno;
636                 return PyErr_SetFromErrno(PyExc_OSError);
637         }
638
639         fd_close(fsp);
640         TALLOC_FREE(frame);
641
642         Py_RETURN_NONE;
643 }
644
645 /*
646   unlink a file
647  */
648 static PyObject *py_smbd_unlink(PyObject *self, PyObject *args, PyObject *kwargs)
649 {
650         const char * const kwnames[] = {
651                 "fname",
652                 "session_info",
653                 "service",
654                 NULL
655         };
656         connection_struct *conn;
657         int ret;
658         struct smb_filename *smb_fname = NULL;
659         struct smb_filename *parent_fname = NULL;
660         struct smb_filename *at_fname = NULL;
661         PyObject *py_session = Py_None;
662         struct auth_session_info *session_info = NULL;
663         char *fname, *service = NULL;
664         TALLOC_CTX *frame;
665         NTSTATUS status;
666
667         frame = talloc_stackframe();
668
669         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|z",
670                                          discard_const_p(char *, kwnames),
671                                          &fname,
672                                          &py_session ,
673                                          &service)) {
674                 TALLOC_FREE(frame);
675                 return NULL;
676         }
677
678         if (!py_check_dcerpc_type(py_session,
679                                   "samba.dcerpc.auth",
680                                   "session_info")) {
681                 TALLOC_FREE(frame);
682                 return NULL;
683         }
684         session_info = pytalloc_get_type(py_session,
685                                          struct auth_session_info);
686         if (session_info == NULL) {
687                 PyErr_Format(PyExc_TypeError,
688                              "Expected auth_session_info for session_info argument got %s",
689                              pytalloc_get_name(py_session));
690                 TALLOC_FREE(frame);
691                 return NULL;
692         }
693
694         conn = get_conn_tos(service, session_info);
695         if (!conn) {
696                 TALLOC_FREE(frame);
697                 return NULL;
698         }
699
700         smb_fname = synthetic_smb_fname_split(frame,
701                                         fname,
702                                         lp_posix_pathnames());
703         if (smb_fname == NULL) {
704                 TALLOC_FREE(frame);
705                 return PyErr_NoMemory();
706         }
707
708         status = parent_pathref(frame,
709                                 conn->cwd_fsp,
710                                 smb_fname,
711                                 &parent_fname,
712                                 &at_fname);
713         if (!NT_STATUS_IS_OK(status)) {
714                 TALLOC_FREE(frame);
715                 return PyErr_NoMemory();
716         }
717
718         ret = SMB_VFS_UNLINKAT(conn,
719                         parent_fname->fsp,
720                         at_fname,
721                         0);
722         if (ret != 0) {
723                 TALLOC_FREE(frame);
724                 errno = ret;
725                 return PyErr_SetFromErrno(PyExc_OSError);
726         }
727
728         TALLOC_FREE(frame);
729
730         Py_RETURN_NONE;
731 }
732
733 /*
734   check if we have ACL support
735  */
736 static PyObject *py_smbd_have_posix_acls(PyObject *self,
737                 PyObject *Py_UNUSED(ignored))
738 {
739 #ifdef HAVE_POSIX_ACLS
740         return PyBool_FromLong(true);
741 #else
742         return PyBool_FromLong(false);
743 #endif
744 }
745
746 /*
747   set the NT ACL on a file
748  */
749 static PyObject *py_smbd_set_nt_acl(PyObject *self, PyObject *args, PyObject *kwargs)
750 {
751         const char * const kwnames[] = {
752                 "fname",
753                 "security_info_sent",
754                 "sd",
755                 "session_info",
756                 "service",
757                 NULL
758         };
759
760         NTSTATUS status;
761         char *fname, *service = NULL;
762         int security_info_sent;
763         PyObject *py_sd;
764         struct security_descriptor *sd;
765         PyObject *py_session = Py_None;
766         struct auth_session_info *session_info = NULL;
767         connection_struct *conn;
768         TALLOC_CTX *frame;
769
770         frame = talloc_stackframe();
771
772         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siOO|z",
773                                          discard_const_p(char *, kwnames),
774                                          &fname,
775                                          &security_info_sent,
776                                          &py_sd,
777                                          &py_session,
778                                          &service)) {
779                 TALLOC_FREE(frame);
780                 return NULL;
781         }
782
783         if (!py_check_dcerpc_type(py_sd, "samba.dcerpc.security", "descriptor")) {
784                 TALLOC_FREE(frame);
785                 return NULL;
786         }
787
788         if (!py_check_dcerpc_type(py_session,
789                                   "samba.dcerpc.auth",
790                                   "session_info")) {
791                 TALLOC_FREE(frame);
792                 return NULL;
793         }
794         session_info = pytalloc_get_type(py_session,
795                                          struct auth_session_info);
796         if (session_info == NULL) {
797                 PyErr_Format(PyExc_TypeError,
798                              "Expected auth_session_info for session_info argument got %s",
799                              pytalloc_get_name(py_session));
800                 return NULL;
801         }
802
803         conn = get_conn_tos(service, session_info);
804         if (!conn) {
805                 TALLOC_FREE(frame);
806                 return NULL;
807         }
808
809         sd = pytalloc_get_type(py_sd, struct security_descriptor);
810
811         status = set_nt_acl_conn(fname, security_info_sent, sd, conn);
812         TALLOC_FREE(frame);
813         if (NT_STATUS_IS_ERR(status)) {
814                 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
815                         /*
816                          * This will show up as a FileNotFoundError in python.
817                          */
818                         PyErr_SetFromErrnoWithFilename(PyExc_OSError, fname);
819                 } else {
820                         PyErr_SetNTSTATUS(status);
821                 }
822                 return NULL;
823         }
824
825         Py_RETURN_NONE;
826 }
827
828 /*
829   Return the NT ACL on a file
830  */
831 static PyObject *py_smbd_get_nt_acl(PyObject *self, PyObject *args, PyObject *kwargs)
832 {
833         const char * const kwnames[] = {
834                 "fname",
835                 "security_info_wanted",
836                 "session_info",
837                 "service",
838                 NULL
839         };
840         char *fname, *service = NULL;
841         int security_info_wanted;
842         PyObject *py_sd;
843         struct security_descriptor *sd;
844         TALLOC_CTX *frame = talloc_stackframe();
845         PyObject *py_session = Py_None;
846         struct auth_session_info *session_info = NULL;
847         connection_struct *conn;
848         NTSTATUS status;
849         int ret = 1;
850
851         ret = PyArg_ParseTupleAndKeywords(args,
852                                           kwargs,
853                                           "siO|z",
854                                           discard_const_p(char *, kwnames),
855                                           &fname,
856                                           &security_info_wanted,
857                                           &py_session,
858                                           &service);
859         if (!ret) {
860                 TALLOC_FREE(frame);
861                 return NULL;
862         }
863
864         if (!py_check_dcerpc_type(py_session,
865                                   "samba.dcerpc.auth",
866                                   "session_info")) {
867                 TALLOC_FREE(frame);
868                 return NULL;
869         }
870         session_info = pytalloc_get_type(py_session,
871                                          struct auth_session_info);
872         if (session_info == NULL) {
873                 PyErr_Format(
874                         PyExc_TypeError,
875                         "Expected auth_session_info for "
876                         "session_info argument got %s",
877                         pytalloc_get_name(py_session));
878                 TALLOC_FREE(frame);
879                 return NULL;
880         }
881
882         conn = get_conn_tos(service, session_info);
883         if (!conn) {
884                 TALLOC_FREE(frame);
885                 return NULL;
886         }
887
888         status = get_nt_acl_conn(frame, fname, conn, security_info_wanted, &sd);
889         if (NT_STATUS_IS_ERR(status)) {
890                 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
891                         /*
892                          * This will show up as a FileNotFoundError in python,
893                          * from which samba-tool can at least produce a short
894                          * message containing the problematic filename.
895                          */
896                         PyErr_SetFromErrnoWithFilename(PyExc_OSError, fname);
897                 } else {
898                         PyErr_SetNTSTATUS(status);
899                 }
900                 TALLOC_FREE(frame);
901                 return NULL;
902         }
903
904         py_sd = py_return_ndr_struct("samba.dcerpc.security", "descriptor", sd, sd);
905
906         TALLOC_FREE(frame);
907
908         return py_sd;
909 }
910
911 /*
912   set the posix (or similar) ACL on a file
913  */
914 static PyObject *py_smbd_set_sys_acl(PyObject *self, PyObject *args, PyObject *kwargs)
915 {
916         const char * const kwnames[] = {
917                 "fname",
918                 "acl_type",
919                 "acl",
920                 "session_info",
921                 "service",
922                 NULL
923         };
924         TALLOC_CTX *frame = talloc_stackframe();
925         int ret;
926         char *fname, *service = NULL;
927         PyObject *py_acl;
928         PyObject *py_session = Py_None;
929         struct auth_session_info *session_info = NULL;
930         struct smb_acl_t *acl;
931         int acl_type;
932         connection_struct *conn;
933
934         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siOO|z",
935                                          discard_const_p(char *, kwnames),
936                                          &fname,
937                                          &acl_type,
938                                          &py_acl,
939                                          &py_session,
940                                          &service)) {
941                 TALLOC_FREE(frame);
942                 return NULL;
943         }
944
945         if (!py_check_dcerpc_type(py_acl, "samba.dcerpc.smb_acl", "t")) {
946                 TALLOC_FREE(frame);
947                 return NULL;
948         }
949
950         if (!py_check_dcerpc_type(py_session,
951                                   "samba.dcerpc.auth",
952                                   "session_info")) {
953                 TALLOC_FREE(frame);
954                 return NULL;
955         }
956         session_info = pytalloc_get_type(py_session,
957                                          struct auth_session_info);
958         if (session_info == NULL) {
959                 PyErr_Format(PyExc_TypeError,
960                              "Expected auth_session_info for session_info argument got %s",
961                              pytalloc_get_name(py_session));
962                 TALLOC_FREE(frame);
963                 return NULL;
964         }
965
966         conn = get_conn_tos(service, session_info);
967         if (!conn) {
968                 TALLOC_FREE(frame);
969                 return NULL;
970         }
971
972         acl = pytalloc_get_type(py_acl, struct smb_acl_t);
973
974         ret = set_sys_acl_conn(fname, acl_type, acl, conn);
975         if (ret != 0) {
976                 TALLOC_FREE(frame);
977                 errno = ret;
978                 return PyErr_SetFromErrno(PyExc_OSError);
979         }
980
981         TALLOC_FREE(frame);
982         Py_RETURN_NONE;
983 }
984
985 /*
986   Return the posix (or similar) ACL on a file
987  */
988 static PyObject *py_smbd_get_sys_acl(PyObject *self, PyObject *args, PyObject *kwargs)
989 {
990         const char * const kwnames[] = {
991                 "fname",
992                 "acl_type",
993                 "session_info",
994                 "service",
995                 NULL
996         };
997         char *fname;
998         PyObject *py_acl;
999         PyObject *py_session = Py_None;
1000         struct auth_session_info *session_info = NULL;
1001         struct smb_acl_t *acl;
1002         int acl_type;
1003         TALLOC_CTX *frame = talloc_stackframe();
1004         connection_struct *conn;
1005         char *service = NULL;
1006         struct smb_filename *smb_fname = NULL;
1007         NTSTATUS status;
1008
1009         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO|z",
1010                                          discard_const_p(char *, kwnames),
1011                                          &fname,
1012                                          &acl_type,
1013                                          &py_session,
1014                                          &service)) {
1015                 TALLOC_FREE(frame);
1016                 return NULL;
1017         }
1018
1019         if (!py_check_dcerpc_type(py_session,
1020                                   "samba.dcerpc.auth",
1021                                   "session_info")) {
1022                 TALLOC_FREE(frame);
1023                 return NULL;
1024         }
1025         session_info = pytalloc_get_type(py_session,
1026                                          struct auth_session_info);
1027         if (session_info == NULL) {
1028                 PyErr_Format(PyExc_TypeError,
1029                              "Expected auth_session_info for session_info argument got %s",
1030                              pytalloc_get_name(py_session));
1031                 TALLOC_FREE(frame);
1032                 return NULL;
1033         }
1034
1035         conn = get_conn_tos(service, session_info);
1036         if (!conn) {
1037                 TALLOC_FREE(frame);
1038                 return NULL;
1039         }
1040
1041         smb_fname = synthetic_smb_fname_split(frame,
1042                                         fname,
1043                                         lp_posix_pathnames());
1044         if (smb_fname == NULL) {
1045                 TALLOC_FREE(frame);
1046                 return NULL;
1047         }
1048
1049         status = openat_pathref_fsp(conn->cwd_fsp, smb_fname);
1050         if (!NT_STATUS_IS_OK(status)) {
1051                 TALLOC_FREE(frame);
1052                 PyErr_SetNTSTATUS(status);
1053                 return NULL;
1054         }
1055
1056         acl = SMB_VFS_SYS_ACL_GET_FD(smb_fname->fsp, acl_type, frame);
1057         if (!acl) {
1058                 TALLOC_FREE(frame);
1059                 return PyErr_SetFromErrno(PyExc_OSError);
1060         }
1061
1062         status = fd_close(smb_fname->fsp);
1063         if (!NT_STATUS_IS_OK(status)) {
1064                 TALLOC_FREE(frame);
1065                 PyErr_SetNTSTATUS(status);
1066                 return NULL;
1067         }
1068
1069         py_acl = py_return_ndr_struct("samba.dcerpc.smb_acl", "t", acl, acl);
1070
1071         TALLOC_FREE(frame);
1072
1073         return py_acl;
1074 }
1075
1076 static PyObject *py_smbd_mkdir(PyObject *self, PyObject *args, PyObject *kwargs)
1077 {
1078         const char * const kwnames[] = {
1079                 "fname",
1080                 "session_info",
1081                 "service",
1082                 NULL
1083         };
1084         char *fname, *service = NULL;
1085         PyObject *py_session = Py_None;
1086         struct auth_session_info *session_info = NULL;
1087         TALLOC_CTX *frame = talloc_stackframe();
1088         struct connection_struct *conn = NULL;
1089         struct smb_filename *smb_fname = NULL;
1090         struct smb_filename *parent_fname = NULL;
1091         struct smb_filename *base_name = NULL;
1092         NTSTATUS status;
1093         int ret;
1094         mode_t saved_umask;
1095
1096         if (!PyArg_ParseTupleAndKeywords(args,
1097                                          kwargs,
1098                                          "sO|z",
1099                                          discard_const_p(char *,
1100                                                          kwnames),
1101                                          &fname,
1102                                          &py_session,
1103                                          &service)) {
1104                 TALLOC_FREE(frame);
1105                 return NULL;
1106         }
1107
1108         if (!py_check_dcerpc_type(py_session,
1109                                   "samba.dcerpc.auth",
1110                                   "session_info")) {
1111                 TALLOC_FREE(frame);
1112                 return NULL;
1113         }
1114         session_info = pytalloc_get_type(py_session,
1115                                          struct auth_session_info);
1116         if (session_info == NULL) {
1117                 PyErr_Format(PyExc_TypeError,
1118                              "Expected auth_session_info for session_info argument got %s",
1119                              pytalloc_get_name(py_session));
1120                 TALLOC_FREE(frame);
1121                 return NULL;
1122         }
1123
1124         conn = get_conn_tos(service, session_info);
1125         if (!conn) {
1126                 TALLOC_FREE(frame);
1127                 return NULL;
1128         }
1129
1130         smb_fname = synthetic_smb_fname(talloc_tos(),
1131                                         fname,
1132                                         NULL,
1133                                         NULL,
1134                                         0,
1135                                         lp_posix_pathnames() ?
1136                                         SMB_FILENAME_POSIX_PATH : 0);
1137
1138         if (smb_fname == NULL) {
1139                 TALLOC_FREE(frame);
1140                 return NULL;
1141         }
1142
1143         status = parent_pathref(talloc_tos(),
1144                                 conn->cwd_fsp,
1145                                 smb_fname,
1146                                 &parent_fname,
1147                                 &base_name);
1148         if (!NT_STATUS_IS_OK(status)) {
1149                 TALLOC_FREE(frame);
1150                 return NULL;
1151         }
1152
1153         /* we want total control over the permissions on created files,
1154            so set our umask to 0 */
1155         saved_umask = umask(0);
1156
1157         ret = SMB_VFS_MKDIRAT(conn,
1158                         parent_fname->fsp,
1159                         base_name,
1160                         00755);
1161
1162         umask(saved_umask);
1163
1164         if (ret == -1) {
1165                 DBG_ERR("mkdirat error=%d (%s)\n", errno, strerror(errno));
1166                 TALLOC_FREE(frame);
1167                 return NULL;
1168         }
1169
1170         TALLOC_FREE(frame);
1171         Py_RETURN_NONE;
1172 }
1173
1174
1175 /*
1176   Create an empty file
1177  */
1178 static PyObject *py_smbd_create_file(PyObject *self, PyObject *args, PyObject *kwargs)
1179 {
1180         const char * const kwnames[] = {
1181                 "fname",
1182                 "session_info",
1183                 "service",
1184                 NULL
1185         };
1186         char *fname, *service = NULL;
1187         PyObject *py_session = Py_None;
1188         struct auth_session_info *session_info = NULL;
1189         TALLOC_CTX *frame = talloc_stackframe();
1190         struct connection_struct *conn = NULL;
1191         struct files_struct *fsp = NULL;
1192         NTSTATUS status;
1193
1194         if (!PyArg_ParseTupleAndKeywords(args,
1195                                          kwargs,
1196                                          "sO|z",
1197                                          discard_const_p(char *,
1198                                                          kwnames),
1199                                          &fname,
1200                                          &py_session,
1201                                          &service)) {
1202                 TALLOC_FREE(frame);
1203                 return NULL;
1204         }
1205
1206         if (!py_check_dcerpc_type(py_session,
1207                                   "samba.dcerpc.auth",
1208                                   "session_info")) {
1209                 TALLOC_FREE(frame);
1210                 return NULL;
1211         }
1212         session_info = pytalloc_get_type(py_session,
1213                                          struct auth_session_info);
1214         if (session_info == NULL) {
1215                 PyErr_Format(PyExc_TypeError,
1216                              "Expected auth_session_info for session_info argument got %s",
1217                              pytalloc_get_name(py_session));
1218                 TALLOC_FREE(frame);
1219                 return NULL;
1220         }
1221
1222         conn = get_conn_tos(service, session_info);
1223         if (!conn) {
1224                 TALLOC_FREE(frame);
1225                 return NULL;
1226         }
1227
1228         status = init_files_struct(frame,
1229                                    fname,
1230                                    conn,
1231                                    O_CREAT|O_EXCL|O_RDWR,
1232                                    &fsp);
1233         if (!NT_STATUS_IS_OK(status)) {
1234                 DBG_ERR("init_files_struct failed: %s\n",
1235                         nt_errstr(status));
1236         } else if (fsp != NULL) {
1237                 fd_close(fsp);
1238         }
1239
1240         TALLOC_FREE(frame);
1241         PyErr_NTSTATUS_NOT_OK_RAISE(status);
1242         Py_RETURN_NONE;
1243 }
1244
1245
1246 static PyMethodDef py_smbd_methods[] = {
1247         { "have_posix_acls",
1248                 (PyCFunction)py_smbd_have_posix_acls, METH_NOARGS,
1249                 NULL },
1250         { "set_simple_acl",
1251                 PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_set_simple_acl),
1252                 METH_VARARGS|METH_KEYWORDS,
1253                 NULL },
1254         { "set_nt_acl",
1255                 PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_set_nt_acl),
1256                 METH_VARARGS|METH_KEYWORDS,
1257                 NULL },
1258         { "get_nt_acl",
1259                 PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_get_nt_acl),
1260                 METH_VARARGS|METH_KEYWORDS,
1261                 NULL },
1262         { "get_sys_acl",
1263                 PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_get_sys_acl),
1264                 METH_VARARGS|METH_KEYWORDS,
1265                 NULL },
1266         { "set_sys_acl",
1267                 PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_set_sys_acl),
1268                 METH_VARARGS|METH_KEYWORDS,
1269                 NULL },
1270         { "chown",
1271                 PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_chown),
1272                 METH_VARARGS|METH_KEYWORDS,
1273                 NULL },
1274         { "unlink",
1275                 PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_unlink),
1276                 METH_VARARGS|METH_KEYWORDS,
1277                 NULL },
1278         { "mkdir",
1279                 PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_mkdir),
1280                 METH_VARARGS|METH_KEYWORDS,
1281                 NULL },
1282         { "create_file",
1283                 PY_DISCARD_FUNC_SIG(PyCFunction, py_smbd_create_file),
1284                 METH_VARARGS|METH_KEYWORDS,
1285                 NULL },
1286         {0}
1287 };
1288
1289 void initsmbd(void);
1290
1291 static struct PyModuleDef moduledef = {
1292     PyModuleDef_HEAD_INIT,
1293     .m_name = "smbd",
1294     .m_doc = "Python bindings for the smbd file server.",
1295     .m_size = -1,
1296     .m_methods = py_smbd_methods,
1297 };
1298
1299 MODULE_INIT_FUNC(smbd)
1300 {
1301         PyObject *m = NULL;
1302
1303         m = PyModule_Create(&moduledef);
1304         return m;
1305 }