Line data Source code
1 : /*
2 : * Unix SMB/CIFS implementation.
3 : * Samba VFS module for error injection in VFS calls
4 : * Copyright (C) Christof Schmitt 2017
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "smbd/smbd.h"
22 :
23 : #undef DBGC_CLASS
24 : #define DBGC_CLASS DBGC_VFS
25 :
26 : struct unix_error_map {
27 : const char *err_str;
28 : int error;
29 : } unix_error_map_array[] = {
30 : { "ESTALE", ESTALE },
31 : { "EBADF", EBADF },
32 : { "EINTR", EINTR },
33 : { "EACCES", EACCES },
34 : };
35 :
36 0 : static int find_unix_error_from_string(const char *err_str)
37 : {
38 : size_t i;
39 :
40 0 : for (i = 0; i < ARRAY_SIZE(unix_error_map_array); i++) {
41 0 : struct unix_error_map *m = &unix_error_map_array[i];
42 :
43 0 : if (strequal(err_str, m->err_str)) {
44 0 : return m->error;
45 : }
46 : }
47 :
48 0 : return 0;
49 : }
50 :
51 0 : static int inject_unix_error(const char *vfs_func, vfs_handle_struct *handle)
52 : {
53 : const char *err_str;
54 :
55 0 : err_str = lp_parm_const_string(SNUM(handle->conn),
56 : "error_inject", vfs_func, NULL);
57 :
58 0 : if (err_str != NULL) {
59 : int error;
60 :
61 0 : error = find_unix_error_from_string(err_str);
62 0 : if (error != 0) {
63 0 : DBG_WARNING("Returning error %s for VFS function %s\n",
64 : err_str, vfs_func);
65 0 : return error;
66 : }
67 :
68 0 : if (strequal(err_str, "panic")) {
69 0 : DBG_ERR("Panic in VFS function %s\n", vfs_func);
70 0 : smb_panic("error_inject");
71 : }
72 :
73 0 : DBG_ERR("Unknown error inject %s requested "
74 : "for vfs function %s\n", err_str, vfs_func);
75 : }
76 :
77 0 : return 0;
78 : }
79 :
80 0 : static int vfs_error_inject_chdir(vfs_handle_struct *handle,
81 : const struct smb_filename *smb_fname)
82 : {
83 : int error;
84 :
85 0 : error = inject_unix_error("chdir", handle);
86 0 : if (error != 0) {
87 0 : errno = error;
88 0 : return -1;
89 : }
90 :
91 0 : return SMB_VFS_NEXT_CHDIR(handle, smb_fname);
92 : }
93 :
94 0 : static ssize_t vfs_error_inject_pwrite(vfs_handle_struct *handle,
95 : files_struct *fsp,
96 : const void *data,
97 : size_t n,
98 : off_t offset)
99 : {
100 : int error;
101 :
102 0 : error = inject_unix_error("pwrite", handle);
103 0 : if (error != 0) {
104 0 : errno = error;
105 0 : return -1;
106 : }
107 :
108 0 : return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
109 : }
110 :
111 0 : static int vfs_error_inject_openat(struct vfs_handle_struct *handle,
112 : const struct files_struct *dirfsp,
113 : const struct smb_filename *smb_fname,
114 : files_struct *fsp,
115 : const struct vfs_open_how *how)
116 : {
117 0 : int error = inject_unix_error("openat", handle);
118 0 : int dirfsp_flags = (O_NOFOLLOW|O_DIRECTORY);
119 : bool return_error;
120 :
121 : #ifdef O_PATH
122 0 : dirfsp_flags |= O_PATH;
123 : #else
124 : #ifdef O_SEARCH
125 : dirfsp_flags |= O_SEARCH;
126 : #endif
127 : #endif
128 :
129 0 : return_error = (error != 0);
130 0 : return_error &= !fsp->fsp_flags.is_pathref;
131 0 : return_error &= ((how->flags & dirfsp_flags) != dirfsp_flags);
132 :
133 0 : if (return_error) {
134 0 : errno = error;
135 0 : return -1;
136 : }
137 0 : return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, how);
138 : }
139 :
140 0 : static int vfs_error_inject_unlinkat(struct vfs_handle_struct *handle,
141 : struct files_struct *dirfsp,
142 : const struct smb_filename *smb_fname,
143 : int flags)
144 : {
145 0 : struct smb_filename *full_fname = NULL;
146 0 : struct smb_filename *parent_fname = NULL;
147 0 : int error = inject_unix_error("unlinkat", handle);
148 : int ret;
149 : NTSTATUS status;
150 :
151 0 : if (error == 0) {
152 0 : return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
153 : }
154 :
155 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
156 : dirfsp,
157 : smb_fname);
158 0 : if (full_fname == NULL) {
159 0 : return -1;
160 : }
161 :
162 0 : status = SMB_VFS_PARENT_PATHNAME(handle->conn,
163 : full_fname, /* TALLOC_CTX. */
164 : full_fname,
165 : &parent_fname,
166 : NULL);
167 0 : if (!NT_STATUS_IS_OK(status)) {
168 0 : TALLOC_FREE(full_fname);
169 0 : errno = map_errno_from_nt_status(status);
170 0 : return -1;
171 : }
172 :
173 0 : ret = SMB_VFS_STAT(handle->conn, parent_fname);
174 0 : if (ret != 0) {
175 0 : TALLOC_FREE(full_fname);
176 0 : return -1;
177 : }
178 :
179 0 : if (parent_fname->st.st_ex_uid == get_current_uid(dirfsp->conn)) {
180 0 : return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
181 : }
182 :
183 0 : errno = error;
184 0 : return -1;
185 : }
186 :
187 : static struct vfs_fn_pointers vfs_error_inject_fns = {
188 : .chdir_fn = vfs_error_inject_chdir,
189 : .pwrite_fn = vfs_error_inject_pwrite,
190 : .openat_fn = vfs_error_inject_openat,
191 : .unlinkat_fn = vfs_error_inject_unlinkat,
192 : };
193 :
194 : static_decl_vfs;
195 26 : NTSTATUS vfs_error_inject_init(TALLOC_CTX *ctx)
196 : {
197 26 : return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "error_inject",
198 : &vfs_error_inject_fns);
199 : }
|