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 : { "EROFS", EROFS },
35 : };
36 :
37 25 : static int find_unix_error_from_string(const char *err_str)
38 : {
39 : size_t i;
40 :
41 84 : for (i = 0; i < ARRAY_SIZE(unix_error_map_array); i++) {
42 84 : struct unix_error_map *m = &unix_error_map_array[i];
43 :
44 84 : if (strequal(err_str, m->err_str)) {
45 25 : return m->error;
46 : }
47 : }
48 :
49 0 : return 0;
50 : }
51 :
52 401 : static int inject_unix_error(const char *vfs_func, vfs_handle_struct *handle)
53 : {
54 : const char *err_str;
55 : int error;
56 :
57 401 : err_str = lp_parm_const_string(SNUM(handle->conn),
58 : "error_inject", vfs_func, NULL);
59 401 : if (err_str == NULL) {
60 376 : return 0;
61 : }
62 :
63 25 : error = find_unix_error_from_string(err_str);
64 25 : if (error != 0) {
65 25 : DBG_WARNING("Returning error %s for VFS function %s\n",
66 : err_str, vfs_func);
67 25 : return error;
68 : }
69 :
70 0 : if (strequal(err_str, "panic")) {
71 0 : DBG_ERR("Panic in VFS function %s\n", vfs_func);
72 0 : smb_panic("error_inject");
73 : }
74 :
75 0 : DBG_ERR("Unknown error inject %s requested "
76 : "for vfs function %s\n", err_str, vfs_func);
77 :
78 0 : return 0;
79 : }
80 :
81 89 : static int vfs_error_inject_chdir(vfs_handle_struct *handle,
82 : const struct smb_filename *smb_fname)
83 : {
84 : int error;
85 :
86 89 : error = inject_unix_error("chdir", handle);
87 89 : if (error != 0) {
88 16 : errno = error;
89 16 : return -1;
90 : }
91 :
92 73 : return SMB_VFS_NEXT_CHDIR(handle, smb_fname);
93 : }
94 :
95 0 : static ssize_t vfs_error_inject_pwrite(vfs_handle_struct *handle,
96 : files_struct *fsp,
97 : const void *data,
98 : size_t n,
99 : off_t offset)
100 : {
101 : int error;
102 :
103 0 : error = inject_unix_error("pwrite", handle);
104 0 : if (error != 0) {
105 0 : errno = error;
106 0 : return -1;
107 : }
108 :
109 0 : return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
110 : }
111 :
112 155 : static int vfs_error_inject_openat(struct vfs_handle_struct *handle,
113 : const struct files_struct *dirfsp,
114 : const struct smb_filename *smb_fname,
115 : files_struct *fsp,
116 : const struct vfs_open_how *how)
117 : {
118 155 : int error = inject_unix_error("openat", handle);
119 155 : int create_error = inject_unix_error("openat_create", handle);
120 155 : int dirfsp_flags = (O_NOFOLLOW|O_DIRECTORY);
121 : bool return_error;
122 :
123 : #ifdef O_PATH
124 113 : dirfsp_flags |= O_PATH;
125 : #else
126 : #ifdef O_SEARCH
127 : dirfsp_flags |= O_SEARCH;
128 : #endif
129 : #endif
130 :
131 155 : if ((create_error != 0) && (how->flags & O_CREAT)) {
132 2 : struct stat_ex st = {
133 : .st_ex_nlink = 0,
134 : };
135 : int ret;
136 :
137 2 : ret = SMB_VFS_FSTATAT(handle->conn,
138 : dirfsp,
139 : smb_fname,
140 : &st,
141 : AT_SYMLINK_NOFOLLOW);
142 :
143 2 : if ((ret == -1) && (errno == ENOENT)) {
144 2 : errno = create_error;
145 2 : return -1;
146 : }
147 : }
148 :
149 153 : return_error = (error != 0);
150 153 : return_error &= !fsp->fsp_flags.is_pathref;
151 153 : return_error &= ((how->flags & dirfsp_flags) != dirfsp_flags);
152 :
153 153 : if (return_error) {
154 0 : errno = error;
155 0 : return -1;
156 : }
157 153 : return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, how);
158 : }
159 :
160 2 : static int vfs_error_inject_unlinkat(struct vfs_handle_struct *handle,
161 : struct files_struct *dirfsp,
162 : const struct smb_filename *smb_fname,
163 : int flags)
164 : {
165 2 : struct smb_filename *full_fname = NULL;
166 2 : struct smb_filename *parent_fname = NULL;
167 2 : int error = inject_unix_error("unlinkat", handle);
168 : int ret;
169 : NTSTATUS status;
170 :
171 2 : if (error == 0) {
172 1 : return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
173 : }
174 :
175 1 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
176 : dirfsp,
177 : smb_fname);
178 1 : if (full_fname == NULL) {
179 0 : return -1;
180 : }
181 :
182 1 : status = SMB_VFS_PARENT_PATHNAME(handle->conn,
183 : full_fname, /* TALLOC_CTX. */
184 : full_fname,
185 : &parent_fname,
186 : NULL);
187 1 : if (!NT_STATUS_IS_OK(status)) {
188 0 : TALLOC_FREE(full_fname);
189 0 : errno = map_errno_from_nt_status(status);
190 0 : return -1;
191 : }
192 :
193 1 : ret = SMB_VFS_STAT(handle->conn, parent_fname);
194 1 : if (ret != 0) {
195 0 : TALLOC_FREE(full_fname);
196 0 : return -1;
197 : }
198 :
199 1 : if (parent_fname->st.st_ex_uid == get_current_uid(dirfsp->conn)) {
200 1 : return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
201 : }
202 :
203 0 : errno = error;
204 0 : return -1;
205 : }
206 :
207 : static struct vfs_fn_pointers vfs_error_inject_fns = {
208 : .chdir_fn = vfs_error_inject_chdir,
209 : .pwrite_fn = vfs_error_inject_pwrite,
210 : .openat_fn = vfs_error_inject_openat,
211 : .unlinkat_fn = vfs_error_inject_unlinkat,
212 : };
213 :
214 : static_decl_vfs;
215 43 : NTSTATUS vfs_error_inject_init(TALLOC_CTX *ctx)
216 : {
217 43 : return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "error_inject",
218 : &vfs_error_inject_fns);
219 : }
|