/src/samba/source3/smbd/smb2_nttrans.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | SMB NT transaction handling |
4 | | Copyright (C) Jeremy Allison 1994-2007 |
5 | | Copyright (C) Stefan (metze) Metzmacher 2003 |
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 "includes.h" |
22 | | #include "system/filesys.h" |
23 | | #include "smbd/smbd.h" |
24 | | #include "smbd/globals.h" |
25 | | #include "fake_file.h" |
26 | | #include "../libcli/security/security.h" |
27 | | #include "../librpc/gen_ndr/ndr_security.h" |
28 | | #include "passdb/lookup_sid.h" |
29 | | #include "auth.h" |
30 | | #include "smbprofile.h" |
31 | | #include "source3/libsmb/proto.h" |
32 | | #include "lib/util_ea.h" |
33 | | #include "librpc/gen_ndr/ndr_quota.h" |
34 | | #include "librpc/gen_ndr/ndr_security.h" |
35 | | |
36 | | extern const struct generic_mapping file_generic_mapping; |
37 | | |
38 | | /********************************************************************* |
39 | | Windows seems to do canonicalization of inheritance bits. Do the |
40 | | same. |
41 | | *********************************************************************/ |
42 | | |
43 | | static void canonicalize_inheritance_bits(struct files_struct *fsp, |
44 | | struct security_descriptor *psd) |
45 | 0 | { |
46 | 0 | bool set_auto_inherited = false; |
47 | | |
48 | | /* |
49 | | * We need to filter out the |
50 | | * SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ |
51 | | * bits. If both are set we store SEC_DESC_DACL_AUTO_INHERITED |
52 | | * as this alters whether SEC_ACE_FLAG_INHERITED_ACE is set |
53 | | * when an ACE is inherited. Otherwise we zero these bits out. |
54 | | * See: |
55 | | * |
56 | | * http://social.msdn.microsoft.com/Forums/eu/os_fileservices/thread/11f77b68-731e-407d-b1b3-064750716531 |
57 | | * |
58 | | * for details. |
59 | | */ |
60 | |
|
61 | 0 | if (!lp_acl_flag_inherited_canonicalization(SNUM(fsp->conn))) { |
62 | 0 | psd->type &= ~SEC_DESC_DACL_AUTO_INHERIT_REQ; |
63 | 0 | return; |
64 | 0 | } |
65 | | |
66 | 0 | if ((psd->type & (SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ)) |
67 | 0 | == (SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ)) { |
68 | 0 | set_auto_inherited = true; |
69 | 0 | } |
70 | |
|
71 | 0 | psd->type &= ~(SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ); |
72 | 0 | if (set_auto_inherited) { |
73 | 0 | psd->type |= SEC_DESC_DACL_AUTO_INHERITED; |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | | /**************************************************************************** |
78 | | Internal fn to set security descriptors. |
79 | | ****************************************************************************/ |
80 | | |
81 | | NTSTATUS set_sd(files_struct *fsp, struct security_descriptor *psd, |
82 | | uint32_t security_info_sent) |
83 | 0 | { |
84 | 0 | files_struct *sd_fsp = NULL; |
85 | 0 | NTSTATUS status; |
86 | 0 | bool refuse; |
87 | |
|
88 | 0 | if (!CAN_WRITE(fsp->conn)) { |
89 | 0 | return NT_STATUS_ACCESS_DENIED; |
90 | 0 | } |
91 | | |
92 | 0 | if (!lp_nt_acl_support(SNUM(fsp->conn))) { |
93 | 0 | return NT_STATUS_OK; |
94 | 0 | } |
95 | | |
96 | 0 | refuse = refuse_symlink_fsp(fsp); |
97 | 0 | if (refuse) { |
98 | 0 | DBG_DEBUG("ACL set on symlink %s denied.\n", |
99 | 0 | fsp_str_dbg(fsp)); |
100 | 0 | return NT_STATUS_ACCESS_DENIED; |
101 | 0 | } |
102 | | |
103 | 0 | if (psd->owner_sid == NULL) { |
104 | 0 | security_info_sent &= ~SECINFO_OWNER; |
105 | 0 | } |
106 | 0 | if (psd->group_sid == NULL) { |
107 | 0 | security_info_sent &= ~SECINFO_GROUP; |
108 | 0 | } |
109 | | |
110 | | /* Ensure we have at least one thing set. */ |
111 | 0 | if ((security_info_sent & (SECINFO_OWNER|SECINFO_GROUP|SECINFO_DACL|SECINFO_SACL)) == 0) { |
112 | | /* Just like W2K3 */ |
113 | 0 | return NT_STATUS_OK; |
114 | 0 | } |
115 | | |
116 | | /* Ensure we have the rights to do this. */ |
117 | 0 | if (security_info_sent & SECINFO_OWNER) { |
118 | 0 | status = check_any_access_fsp(fsp, SEC_STD_WRITE_OWNER); |
119 | 0 | if (!NT_STATUS_IS_OK(status)) { |
120 | 0 | return status; |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | 0 | if (security_info_sent & SECINFO_GROUP) { |
125 | 0 | status = check_any_access_fsp(fsp, SEC_STD_WRITE_OWNER); |
126 | 0 | if (!NT_STATUS_IS_OK(status)) { |
127 | 0 | return status; |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | 0 | if (security_info_sent & SECINFO_DACL) { |
132 | 0 | status = check_any_access_fsp(fsp, SEC_STD_WRITE_DAC); |
133 | 0 | if (!NT_STATUS_IS_OK(status)) { |
134 | 0 | return status; |
135 | 0 | } |
136 | | /* Convert all the generic bits. */ |
137 | 0 | if (psd->dacl) { |
138 | 0 | security_acl_map_generic(psd->dacl, &file_generic_mapping); |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | 0 | if (security_info_sent & SECINFO_SACL) { |
143 | 0 | status = check_any_access_fsp(fsp, SEC_FLAG_SYSTEM_SECURITY); |
144 | 0 | if (!NT_STATUS_IS_OK(status)) { |
145 | 0 | return status; |
146 | 0 | } |
147 | | /* |
148 | | * Setting a SACL also requires WRITE_DAC. |
149 | | * See the smbtorture3 SMB2-SACL test. |
150 | | */ |
151 | 0 | status = check_any_access_fsp(fsp, SEC_STD_WRITE_DAC); |
152 | 0 | if (!NT_STATUS_IS_OK(status)) { |
153 | 0 | return status; |
154 | 0 | } |
155 | | /* Convert all the generic bits. */ |
156 | 0 | if (psd->sacl) { |
157 | 0 | security_acl_map_generic(psd->sacl, &file_generic_mapping); |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | 0 | canonicalize_inheritance_bits(fsp, psd); |
162 | |
|
163 | 0 | if (DEBUGLEVEL >= 10) { |
164 | 0 | DEBUG(10,("set_sd for file %s\n", fsp_str_dbg(fsp))); |
165 | 0 | NDR_PRINT_DEBUG(security_descriptor, psd); |
166 | 0 | } |
167 | |
|
168 | 0 | sd_fsp = metadata_fsp(fsp); |
169 | 0 | status = SMB_VFS_FSET_NT_ACL(sd_fsp, security_info_sent, psd); |
170 | 0 | TALLOC_FREE(psd); |
171 | |
|
172 | 0 | if (NT_STATUS_IS_OK(status)) { |
173 | 0 | notify_fname(fsp->conn, |
174 | 0 | NOTIFY_ACTION_MODIFIED, |
175 | 0 | FILE_NOTIFY_CHANGE_SECURITY, |
176 | 0 | fsp->fsp_name, |
177 | 0 | NULL); |
178 | 0 | } |
179 | |
|
180 | 0 | return status; |
181 | 0 | } |
182 | | |
183 | | static bool check_smb2_posix_chmod_ace(const struct files_struct *fsp, |
184 | | uint32_t security_info_sent, |
185 | | struct security_descriptor *psd, |
186 | | mode_t *pmode) |
187 | 0 | { |
188 | 0 | struct security_ace *ace = NULL; |
189 | 0 | int cmp; |
190 | | |
191 | | /* |
192 | | * This must be an ACL with one ACE containing an |
193 | | * MS NFS style mode entry coming in on a POSIX |
194 | | * handle over SMB2+. |
195 | | */ |
196 | 0 | if (!conn_using_smb2(fsp->conn->sconn)) { |
197 | 0 | return false; |
198 | 0 | } |
199 | | |
200 | 0 | if (!fsp->fsp_flags.posix_open) { |
201 | 0 | return false; |
202 | 0 | } |
203 | | |
204 | 0 | if (!(security_info_sent & SECINFO_DACL)) { |
205 | 0 | return false; |
206 | 0 | } |
207 | | |
208 | 0 | if (psd->dacl == NULL) { |
209 | 0 | return false; |
210 | 0 | } |
211 | | |
212 | 0 | if (psd->dacl->num_aces != 1) { |
213 | 0 | return false; |
214 | 0 | } |
215 | 0 | ace = &psd->dacl->aces[0]; |
216 | |
|
217 | 0 | if (ace->trustee.num_auths != 3) { |
218 | 0 | return false; |
219 | 0 | } |
220 | | |
221 | 0 | cmp = dom_sid_compare_domain(&global_sid_Unix_NFS_Mode, &ace->trustee); |
222 | 0 | if (cmp != 0) { |
223 | 0 | return false; |
224 | 0 | } |
225 | | |
226 | 0 | *pmode = (mode_t)ace->trustee.sub_auths[2]; |
227 | 0 | *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO); |
228 | |
|
229 | 0 | return true; |
230 | 0 | } |
231 | | |
232 | | /**************************************************************************** |
233 | | Internal fn to set security descriptors from a data blob. |
234 | | ****************************************************************************/ |
235 | | |
236 | | NTSTATUS set_sd_blob(files_struct *fsp, uint8_t *data, uint32_t sd_len, |
237 | | uint32_t security_info_sent) |
238 | 0 | { |
239 | 0 | struct security_descriptor *psd = NULL; |
240 | 0 | NTSTATUS status; |
241 | 0 | bool do_chmod = false; |
242 | 0 | mode_t smb2_posix_mode = 0; |
243 | 0 | int ret; |
244 | |
|
245 | 0 | if (sd_len == 0) { |
246 | 0 | return NT_STATUS_INVALID_PARAMETER; |
247 | 0 | } |
248 | | |
249 | 0 | status = unmarshall_sec_desc(talloc_tos(), data, sd_len, &psd); |
250 | |
|
251 | 0 | if (!NT_STATUS_IS_OK(status)) { |
252 | 0 | return status; |
253 | 0 | } |
254 | | |
255 | 0 | do_chmod = check_smb2_posix_chmod_ace(fsp, |
256 | 0 | security_info_sent, |
257 | 0 | psd, |
258 | 0 | &smb2_posix_mode); |
259 | 0 | if (!do_chmod) { |
260 | 0 | return set_sd(fsp, psd, security_info_sent); |
261 | 0 | } |
262 | | |
263 | 0 | TALLOC_FREE(psd); |
264 | |
|
265 | 0 | ret = SMB_VFS_FCHMOD(fsp, smb2_posix_mode); |
266 | 0 | if (ret != 0) { |
267 | 0 | status = map_nt_error_from_unix(errno); |
268 | 0 | DBG_ERR("smb2_posix_chmod [%s] [%04o] failed: %s\n", |
269 | 0 | fsp_str_dbg(fsp), |
270 | 0 | (unsigned)smb2_posix_mode, |
271 | 0 | nt_errstr(status)); |
272 | 0 | return status; |
273 | 0 | } |
274 | | |
275 | 0 | return NT_STATUS_OK; |
276 | 0 | } |
277 | | |
278 | | /**************************************************************************** |
279 | | Copy a file. |
280 | | ****************************************************************************/ |
281 | | |
282 | | NTSTATUS copy_internals(TALLOC_CTX *ctx, |
283 | | connection_struct *conn, |
284 | | struct smb_request *req, |
285 | | struct files_struct *src_dirfsp, |
286 | | struct smb_filename *smb_fname_src, |
287 | | struct files_struct *dst_dirfsp, |
288 | | struct smb_filename *smb_fname_dst, |
289 | | uint32_t attrs) |
290 | 0 | { |
291 | 0 | files_struct *fsp1,*fsp2; |
292 | 0 | uint32_t fattr; |
293 | 0 | int info; |
294 | 0 | off_t ret=-1; |
295 | 0 | NTSTATUS status = NT_STATUS_OK; |
296 | |
|
297 | 0 | if (!CAN_WRITE(conn)) { |
298 | 0 | status = NT_STATUS_MEDIA_WRITE_PROTECTED; |
299 | 0 | goto out; |
300 | 0 | } |
301 | | |
302 | | /* Source must already exist. */ |
303 | 0 | if (!VALID_STAT(smb_fname_src->st)) { |
304 | 0 | status = NT_STATUS_OBJECT_NAME_NOT_FOUND; |
305 | 0 | goto out; |
306 | 0 | } |
307 | | |
308 | | /* Ensure attributes match. */ |
309 | 0 | fattr = fdos_mode(smb_fname_src->fsp); |
310 | 0 | if ((fattr & ~attrs) & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) { |
311 | 0 | status = NT_STATUS_NO_SUCH_FILE; |
312 | 0 | goto out; |
313 | 0 | } |
314 | | |
315 | | /* Disallow if dst file already exists. */ |
316 | 0 | if (VALID_STAT(smb_fname_dst->st)) { |
317 | 0 | status = NT_STATUS_OBJECT_NAME_COLLISION; |
318 | 0 | goto out; |
319 | 0 | } |
320 | | |
321 | | /* No copy from a directory. */ |
322 | 0 | if (S_ISDIR(smb_fname_src->st.st_ex_mode)) { |
323 | 0 | status = NT_STATUS_FILE_IS_A_DIRECTORY; |
324 | 0 | goto out; |
325 | 0 | } |
326 | | |
327 | 0 | DBG_DEBUG("doing file copy %s to %s\n", |
328 | 0 | smb_fname_str_dbg(smb_fname_src), |
329 | 0 | smb_fname_str_dbg(smb_fname_dst)); |
330 | |
|
331 | 0 | status = SMB_VFS_CREATE_FILE( |
332 | 0 | conn, /* conn */ |
333 | 0 | req, /* req */ |
334 | 0 | src_dirfsp, /* dirfsp */ |
335 | 0 | smb_fname_src, /* fname */ |
336 | 0 | FILE_READ_DATA|FILE_READ_ATTRIBUTES| |
337 | 0 | FILE_READ_EA, /* access_mask */ |
338 | 0 | (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */ |
339 | 0 | FILE_SHARE_DELETE), |
340 | 0 | FILE_OPEN, /* create_disposition*/ |
341 | 0 | 0, /* create_options */ |
342 | 0 | FILE_ATTRIBUTE_NORMAL, /* file_attributes */ |
343 | 0 | NO_OPLOCK, /* oplock_request */ |
344 | 0 | NULL, /* lease */ |
345 | 0 | 0, /* allocation_size */ |
346 | 0 | 0, /* private_flags */ |
347 | 0 | NULL, /* sd */ |
348 | 0 | NULL, /* ea_list */ |
349 | 0 | &fsp1, /* result */ |
350 | 0 | &info, /* pinfo */ |
351 | 0 | NULL, NULL); /* create context */ |
352 | |
|
353 | 0 | if (!NT_STATUS_IS_OK(status)) { |
354 | 0 | goto out; |
355 | 0 | } |
356 | | |
357 | 0 | status = SMB_VFS_CREATE_FILE( |
358 | 0 | conn, /* conn */ |
359 | 0 | req, /* req */ |
360 | 0 | dst_dirfsp, /* dirfsp */ |
361 | 0 | smb_fname_dst, /* fname */ |
362 | 0 | FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES| |
363 | 0 | FILE_WRITE_EA, /* access_mask */ |
364 | 0 | (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */ |
365 | 0 | FILE_SHARE_DELETE), |
366 | 0 | FILE_CREATE, /* create_disposition*/ |
367 | 0 | 0, /* create_options */ |
368 | 0 | fattr, /* file_attributes */ |
369 | 0 | NO_OPLOCK, /* oplock_request */ |
370 | 0 | NULL, /* lease */ |
371 | 0 | 0, /* allocation_size */ |
372 | 0 | 0, /* private_flags */ |
373 | 0 | NULL, /* sd */ |
374 | 0 | NULL, /* ea_list */ |
375 | 0 | &fsp2, /* result */ |
376 | 0 | &info, /* pinfo */ |
377 | 0 | NULL, NULL); /* create context */ |
378 | |
|
379 | 0 | if (!NT_STATUS_IS_OK(status)) { |
380 | 0 | close_file_free(NULL, &fsp1, ERROR_CLOSE); |
381 | 0 | goto out; |
382 | 0 | } |
383 | | |
384 | 0 | if (smb_fname_src->st.st_ex_size) { |
385 | 0 | ret = vfs_transfer_file(fsp1, fsp2, smb_fname_src->st.st_ex_size); |
386 | 0 | } |
387 | | |
388 | | /* |
389 | | * As we are opening fsp1 read-only we only expect |
390 | | * an error on close on fsp2 if we are out of space. |
391 | | * Thus we don't look at the error return from the |
392 | | * close of fsp1. |
393 | | */ |
394 | 0 | close_file_free(NULL, &fsp1, NORMAL_CLOSE); |
395 | | |
396 | | /* Ensure the modtime is set correctly on the destination file. */ |
397 | 0 | set_close_write_time(fsp2, smb_fname_src->st.st_ex_mtime); |
398 | |
|
399 | 0 | status = close_file_free(NULL, &fsp2, NORMAL_CLOSE); |
400 | 0 | if (!NT_STATUS_IS_OK(status)) { |
401 | 0 | DBG_WARNING("close_file_free() failed: %s\n", |
402 | 0 | nt_errstr(status)); |
403 | | /* |
404 | | * We can't do much but leak the fsp |
405 | | */ |
406 | 0 | goto out; |
407 | 0 | } |
408 | | |
409 | | /* Grrr. We have to do this as open_file_ntcreate adds FILE_ATTRIBUTE_ARCHIVE when it |
410 | | creates the file. This isn't the correct thing to do in the copy |
411 | | case. JRA */ |
412 | | |
413 | 0 | if (smb_fname_dst->fsp == NULL) { |
414 | 0 | struct smb_filename *pathref = NULL; |
415 | |
|
416 | 0 | status = synthetic_pathref(ctx, |
417 | 0 | conn->cwd_fsp, |
418 | 0 | smb_fname_dst->base_name, |
419 | 0 | smb_fname_dst->stream_name, |
420 | 0 | NULL, |
421 | 0 | smb_fname_dst->twrp, |
422 | 0 | smb_fname_dst->flags, |
423 | 0 | &pathref); |
424 | | |
425 | | /* should we handle NT_STATUS_OBJECT_NAME_NOT_FOUND specially here ???? */ |
426 | 0 | if (!NT_STATUS_IS_OK(status)) { |
427 | 0 | goto out; |
428 | 0 | } |
429 | 0 | file_set_dosmode(conn, pathref, fattr, dst_dirfsp, false); |
430 | 0 | smb_fname_dst->st.st_ex_mode = pathref->st.st_ex_mode; |
431 | 0 | TALLOC_FREE(pathref); |
432 | 0 | } else { |
433 | 0 | file_set_dosmode( |
434 | 0 | conn, smb_fname_dst, fattr, dst_dirfsp, false); |
435 | 0 | } |
436 | | |
437 | 0 | if (ret < (off_t)smb_fname_src->st.st_ex_size) { |
438 | 0 | status = NT_STATUS_DISK_FULL; |
439 | 0 | goto out; |
440 | 0 | } |
441 | 0 | out: |
442 | 0 | if (!NT_STATUS_IS_OK(status)) { |
443 | 0 | DBG_NOTICE("Error %s copy file %s to %s\n", |
444 | 0 | nt_errstr(status), |
445 | 0 | smb_fname_str_dbg(smb_fname_src), |
446 | 0 | smb_fname_str_dbg(smb_fname_dst)); |
447 | 0 | } |
448 | |
|
449 | 0 | return status; |
450 | 0 | } |
451 | | |
452 | | /****************************************************************************** |
453 | | Fake up a completely empty SD. |
454 | | *******************************************************************************/ |
455 | | |
456 | | static NTSTATUS get_null_nt_acl(TALLOC_CTX *mem_ctx, struct security_descriptor **ppsd) |
457 | 0 | { |
458 | 0 | size_t sd_size; |
459 | |
|
460 | 0 | *ppsd = make_standard_sec_desc( mem_ctx, &global_sid_World, &global_sid_World, NULL, &sd_size); |
461 | 0 | if(!*ppsd) { |
462 | 0 | DBG_ERR("Unable to malloc space for security descriptor.\n"); |
463 | 0 | return NT_STATUS_NO_MEMORY; |
464 | 0 | } |
465 | | |
466 | 0 | return NT_STATUS_OK; |
467 | 0 | } |
468 | | |
469 | | /**************************************************************************** |
470 | | Get a security descriptor from the file system, normalize for components |
471 | | requested. |
472 | | ****************************************************************************/ |
473 | | |
474 | | static NTSTATUS smbd_fetch_security_desc(connection_struct *conn, |
475 | | TALLOC_CTX *mem_ctx, |
476 | | files_struct *fsp, |
477 | | uint32_t security_info_wanted, |
478 | | struct security_descriptor **ppsd) |
479 | 0 | { |
480 | 0 | NTSTATUS status; |
481 | 0 | struct security_descriptor *psd = NULL; |
482 | 0 | bool need_to_read_sd = false; |
483 | 0 | bool refuse; |
484 | | |
485 | | /* |
486 | | * Get the permissions to return. |
487 | | */ |
488 | |
|
489 | 0 | if (security_info_wanted & SECINFO_SACL) { |
490 | 0 | status = check_any_access_fsp(fsp, SEC_FLAG_SYSTEM_SECURITY); |
491 | 0 | if (!NT_STATUS_IS_OK(status)) { |
492 | 0 | DBG_DEBUG("Access to SACL denied.\n"); |
493 | 0 | return status; |
494 | 0 | } |
495 | 0 | } |
496 | | |
497 | 0 | if (security_info_wanted & (SECINFO_DACL|SECINFO_OWNER|SECINFO_GROUP)) { |
498 | 0 | status = check_any_access_fsp(fsp, SEC_STD_READ_CONTROL); |
499 | 0 | if (!NT_STATUS_IS_OK(status)) { |
500 | 0 | DBG_DEBUG("Access to DACL, OWNER, or GROUP denied.\n"); |
501 | 0 | return status; |
502 | 0 | } |
503 | 0 | } |
504 | | |
505 | 0 | refuse = refuse_symlink_fsp(fsp); |
506 | 0 | if (refuse) { |
507 | 0 | DBG_DEBUG("ACL get on symlink %s denied.\n", |
508 | 0 | fsp_str_dbg(fsp)); |
509 | 0 | return NT_STATUS_ACCESS_DENIED; |
510 | 0 | } |
511 | | |
512 | 0 | if (security_info_wanted & (SECINFO_DACL|SECINFO_OWNER| |
513 | 0 | SECINFO_GROUP|SECINFO_SACL)) { |
514 | | /* Don't return SECINFO_LABEL if anything else was |
515 | | requested. See bug #8458. */ |
516 | 0 | security_info_wanted &= ~SECINFO_LABEL; |
517 | | |
518 | | /* |
519 | | * Only query the file system SD if the caller asks |
520 | | * for any bits. This allows a caller to open without |
521 | | * READ_CONTROL but still issue a query sd. See |
522 | | * smb2.sdread test. |
523 | | */ |
524 | 0 | need_to_read_sd = true; |
525 | 0 | } |
526 | |
|
527 | 0 | if (lp_nt_acl_support(SNUM(conn)) && |
528 | 0 | ((security_info_wanted & SECINFO_LABEL) == 0) && |
529 | 0 | need_to_read_sd) |
530 | 0 | { |
531 | 0 | files_struct *sd_fsp = metadata_fsp(fsp); |
532 | 0 | status = SMB_VFS_FGET_NT_ACL( |
533 | 0 | sd_fsp, security_info_wanted, mem_ctx, &psd); |
534 | 0 | } else { |
535 | 0 | status = get_null_nt_acl(mem_ctx, &psd); |
536 | 0 | } |
537 | |
|
538 | 0 | if (!NT_STATUS_IS_OK(status)) { |
539 | 0 | return status; |
540 | 0 | } |
541 | | |
542 | 0 | if (!(security_info_wanted & SECINFO_OWNER)) { |
543 | 0 | psd->owner_sid = NULL; |
544 | 0 | } |
545 | 0 | if (!(security_info_wanted & SECINFO_GROUP)) { |
546 | 0 | psd->group_sid = NULL; |
547 | 0 | } |
548 | 0 | if (!(security_info_wanted & SECINFO_DACL)) { |
549 | 0 | psd->type &= ~SEC_DESC_DACL_PRESENT; |
550 | 0 | psd->dacl = NULL; |
551 | 0 | } |
552 | 0 | if (!(security_info_wanted & SECINFO_SACL)) { |
553 | 0 | psd->type &= ~SEC_DESC_SACL_PRESENT; |
554 | 0 | psd->sacl = NULL; |
555 | 0 | } |
556 | | |
557 | | /* If the SACL/DACL is NULL, but was requested, we mark that it is |
558 | | * present in the reply to match Windows behavior */ |
559 | 0 | if (psd->sacl == NULL && |
560 | 0 | security_info_wanted & SECINFO_SACL) |
561 | 0 | psd->type |= SEC_DESC_SACL_PRESENT; |
562 | 0 | if (psd->dacl == NULL && |
563 | 0 | security_info_wanted & SECINFO_DACL) |
564 | 0 | psd->type |= SEC_DESC_DACL_PRESENT; |
565 | |
|
566 | 0 | if (security_info_wanted & SECINFO_LABEL) { |
567 | | /* Like W2K3 return a null object. */ |
568 | 0 | psd->owner_sid = NULL; |
569 | 0 | psd->group_sid = NULL; |
570 | 0 | psd->dacl = NULL; |
571 | 0 | psd->sacl = NULL; |
572 | 0 | psd->type &= ~(SEC_DESC_DACL_PRESENT|SEC_DESC_SACL_PRESENT); |
573 | 0 | } |
574 | |
|
575 | 0 | *ppsd = psd; |
576 | 0 | return NT_STATUS_OK; |
577 | 0 | } |
578 | | |
579 | | /**************************************************************************** |
580 | | Write a security descriptor into marshalled format. |
581 | | ****************************************************************************/ |
582 | | |
583 | | static NTSTATUS smbd_marshall_security_desc(TALLOC_CTX *mem_ctx, |
584 | | files_struct *fsp, |
585 | | struct security_descriptor *psd, |
586 | | uint32_t max_data_count, |
587 | | uint8_t **ppmarshalled_sd, |
588 | | size_t *psd_size) |
589 | 0 | { |
590 | 0 | *psd_size = ndr_size_security_descriptor(psd, 0); |
591 | |
|
592 | 0 | DBG_NOTICE("sd_size = %zu.\n", *psd_size); |
593 | |
|
594 | 0 | if (DEBUGLEVEL >= 10) { |
595 | 0 | DBG_DEBUG("security desc for file %s\n", |
596 | 0 | fsp_str_dbg(fsp)); |
597 | 0 | NDR_PRINT_DEBUG(security_descriptor, psd); |
598 | 0 | } |
599 | |
|
600 | 0 | if (max_data_count < *psd_size) { |
601 | 0 | return NT_STATUS_BUFFER_TOO_SMALL; |
602 | 0 | } |
603 | | |
604 | 0 | return marshall_sec_desc(mem_ctx, |
605 | 0 | psd, |
606 | 0 | ppmarshalled_sd, |
607 | 0 | psd_size); |
608 | 0 | } |
609 | | |
610 | | /**************************************************************************** |
611 | | Reply to query a security descriptor. |
612 | | Callable from SMB1 and SMB2. |
613 | | If it returns NT_STATUS_BUFFER_TOO_SMALL, psd_size is initialized with |
614 | | the required size. |
615 | | ****************************************************************************/ |
616 | | |
617 | | NTSTATUS smbd_do_query_security_desc(connection_struct *conn, |
618 | | TALLOC_CTX *mem_ctx, |
619 | | files_struct *fsp, |
620 | | uint32_t security_info_wanted, |
621 | | uint32_t max_data_count, |
622 | | uint8_t **ppmarshalled_sd, |
623 | | size_t *psd_size) |
624 | 0 | { |
625 | 0 | NTSTATUS status; |
626 | 0 | struct security_descriptor *psd = NULL; |
627 | | |
628 | | /* |
629 | | * Get the permissions to return. |
630 | | */ |
631 | |
|
632 | 0 | status = smbd_fetch_security_desc(conn, |
633 | 0 | mem_ctx, |
634 | 0 | fsp, |
635 | 0 | security_info_wanted, |
636 | 0 | &psd); |
637 | 0 | if (!NT_STATUS_IS_OK(status)) { |
638 | 0 | return status; |
639 | 0 | } |
640 | | |
641 | 0 | status = smbd_marshall_security_desc(mem_ctx, |
642 | 0 | fsp, |
643 | 0 | psd, |
644 | 0 | max_data_count, |
645 | 0 | ppmarshalled_sd, |
646 | 0 | psd_size); |
647 | 0 | TALLOC_FREE(psd); |
648 | 0 | return status; |
649 | 0 | } |
650 | | |
651 | | #ifdef HAVE_SYS_QUOTAS |
652 | | static enum ndr_err_code fill_qtlist_from_sids(TALLOC_CTX *mem_ctx, |
653 | | struct files_struct *fsp, |
654 | | SMB_NTQUOTA_HANDLE *qt_handle, |
655 | | struct dom_sid *sids, |
656 | | uint32_t elems) |
657 | 0 | { |
658 | 0 | uint32_t i; |
659 | 0 | TALLOC_CTX *list_ctx = NULL; |
660 | |
|
661 | 0 | list_ctx = talloc_init("quota_sid_list"); |
662 | |
|
663 | 0 | if (list_ctx == NULL) { |
664 | 0 | DBG_ERR("failed to allocate\n"); |
665 | 0 | return NDR_ERR_ALLOC; |
666 | 0 | } |
667 | | |
668 | 0 | if (qt_handle->quota_list!=NULL) { |
669 | 0 | free_ntquota_list(&(qt_handle->quota_list)); |
670 | 0 | } |
671 | 0 | for (i = 0; i < elems; i++) { |
672 | 0 | SMB_NTQUOTA_STRUCT qt; |
673 | 0 | SMB_NTQUOTA_LIST *list_item; |
674 | 0 | bool ok; |
675 | |
|
676 | 0 | if (!NT_STATUS_IS_OK(vfs_get_ntquota(fsp, |
677 | 0 | SMB_USER_QUOTA_TYPE, |
678 | 0 | &sids[i], &qt))) { |
679 | | /* non fatal error, return empty item in result */ |
680 | 0 | ZERO_STRUCT(qt); |
681 | 0 | continue; |
682 | 0 | } |
683 | | |
684 | | |
685 | 0 | list_item = talloc_zero(list_ctx, SMB_NTQUOTA_LIST); |
686 | 0 | if (list_item == NULL) { |
687 | 0 | DBG_ERR("failed to allocate\n"); |
688 | 0 | return NDR_ERR_ALLOC; |
689 | 0 | } |
690 | | |
691 | 0 | ok = sid_to_uid(&sids[i], &list_item->uid); |
692 | 0 | if (!ok) { |
693 | 0 | struct dom_sid_buf buf; |
694 | 0 | DBG_WARNING("Could not convert SID %s to uid\n", |
695 | 0 | dom_sid_str_buf(&sids[i], &buf)); |
696 | | /* No idea what to return here... */ |
697 | 0 | return NDR_ERR_INVALID_POINTER; |
698 | 0 | } |
699 | | |
700 | 0 | list_item->quotas = talloc_zero(list_item, SMB_NTQUOTA_STRUCT); |
701 | 0 | if (list_item->quotas == NULL) { |
702 | 0 | DBG_ERR("failed to allocate\n"); |
703 | 0 | return NDR_ERR_ALLOC; |
704 | 0 | } |
705 | | |
706 | 0 | *list_item->quotas = qt; |
707 | 0 | list_item->mem_ctx = list_ctx; |
708 | 0 | DLIST_ADD(qt_handle->quota_list, list_item); |
709 | 0 | } |
710 | 0 | qt_handle->tmp_list = qt_handle->quota_list; |
711 | 0 | return NDR_ERR_SUCCESS; |
712 | 0 | } |
713 | | |
714 | | static enum ndr_err_code extract_sids_from_buf(TALLOC_CTX *mem_ctx, |
715 | | uint32_t sidlistlength, |
716 | | DATA_BLOB *sid_buf, |
717 | | struct dom_sid **sids, |
718 | | uint32_t *num) |
719 | 0 | { |
720 | 0 | DATA_BLOB blob; |
721 | 0 | uint32_t i = 0; |
722 | 0 | enum ndr_err_code err; |
723 | |
|
724 | 0 | struct sid_list_elem { |
725 | 0 | struct sid_list_elem *prev, *next; |
726 | 0 | struct dom_sid sid; |
727 | 0 | }; |
728 | |
|
729 | 0 | struct sid_list_elem *sid_list = NULL; |
730 | 0 | struct sid_list_elem *iter = NULL; |
731 | 0 | TALLOC_CTX *list_ctx = talloc_init("sid_list"); |
732 | 0 | if (!list_ctx) { |
733 | 0 | DBG_ERR("OOM\n"); |
734 | 0 | err = NDR_ERR_ALLOC; |
735 | 0 | goto done; |
736 | 0 | } |
737 | | |
738 | 0 | *num = 0; |
739 | 0 | *sids = NULL; |
740 | |
|
741 | 0 | if (sidlistlength) { |
742 | 0 | uint32_t offset = 0; |
743 | 0 | struct ndr_pull *ndr_pull = NULL; |
744 | |
|
745 | 0 | if (sidlistlength > sid_buf->length) { |
746 | 0 | DBG_ERR("sid_list_length 0x%x exceeds " |
747 | 0 | "available bytes %zx\n", |
748 | 0 | sidlistlength, |
749 | 0 | sid_buf->length); |
750 | 0 | err = NDR_ERR_OFFSET; |
751 | 0 | goto done; |
752 | 0 | } |
753 | 0 | while (true) { |
754 | 0 | struct file_get_quota_info info; |
755 | 0 | struct sid_list_elem *item = NULL; |
756 | 0 | uint32_t new_offset = 0; |
757 | 0 | blob.data = sid_buf->data + offset; |
758 | 0 | blob.length = sidlistlength - offset; |
759 | 0 | ndr_pull = ndr_pull_init_blob(&blob, list_ctx); |
760 | 0 | if (!ndr_pull) { |
761 | 0 | DBG_ERR("OOM\n"); |
762 | 0 | err = NDR_ERR_ALLOC; |
763 | 0 | goto done; |
764 | 0 | } |
765 | 0 | err = ndr_pull_file_get_quota_info(ndr_pull, |
766 | 0 | NDR_SCALARS | NDR_BUFFERS, &info); |
767 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(err)) { |
768 | 0 | DBG_ERR("Failed to pull file_get_quota_info " |
769 | 0 | "from sidlist buffer\n"); |
770 | 0 | goto done; |
771 | 0 | } |
772 | 0 | item = talloc_zero(list_ctx, struct sid_list_elem); |
773 | 0 | if (!item) { |
774 | 0 | DBG_ERR("OOM\n"); |
775 | 0 | err = NDR_ERR_ALLOC; |
776 | 0 | goto done; |
777 | 0 | } |
778 | 0 | item->sid = info.sid; |
779 | 0 | DLIST_ADD(sid_list, item); |
780 | 0 | i++; |
781 | 0 | if (i == UINT32_MAX) { |
782 | 0 | DBG_ERR("Integer overflow\n"); |
783 | 0 | err = NDR_ERR_ARRAY_SIZE; |
784 | 0 | goto done; |
785 | 0 | } |
786 | 0 | new_offset = info.next_entry_offset; |
787 | | |
788 | | /* if new_offset == 0 no more sid(s) to read. */ |
789 | 0 | if (new_offset == 0) { |
790 | 0 | break; |
791 | 0 | } |
792 | | |
793 | | /* Integer wrap? */ |
794 | 0 | if ((offset + new_offset) < offset) { |
795 | 0 | DBG_ERR("Integer wrap while adding " |
796 | 0 | "new_offset 0x%x to current " |
797 | 0 | "buffer offset 0x%x\n", |
798 | 0 | new_offset, offset); |
799 | 0 | err = NDR_ERR_OFFSET; |
800 | 0 | goto done; |
801 | 0 | } |
802 | | |
803 | 0 | offset += new_offset; |
804 | | |
805 | | /* check if new offset is outside buffer boundary. */ |
806 | 0 | if (offset >= sidlistlength) { |
807 | 0 | DBG_ERR("bufsize 0x%x exceeded by " |
808 | 0 | "new offset 0x%x)\n", |
809 | 0 | sidlistlength, |
810 | 0 | offset); |
811 | 0 | err = NDR_ERR_OFFSET; |
812 | 0 | goto done; |
813 | 0 | } |
814 | 0 | } |
815 | 0 | *sids = talloc_zero_array(mem_ctx, struct dom_sid, i); |
816 | 0 | if (*sids == NULL) { |
817 | 0 | DBG_ERR("OOM\n"); |
818 | 0 | err = NDR_ERR_ALLOC; |
819 | 0 | goto done; |
820 | 0 | } |
821 | | |
822 | 0 | *num = i; |
823 | |
|
824 | 0 | for (iter = sid_list, i = 0; iter; iter = iter->next, i++) { |
825 | 0 | struct dom_sid_buf buf; |
826 | 0 | (*sids)[i] = iter->sid; |
827 | 0 | DBG_DEBUG("quota SID[%u] %s\n", |
828 | 0 | (unsigned int)i, |
829 | 0 | dom_sid_str_buf(&iter->sid, &buf)); |
830 | 0 | } |
831 | 0 | } |
832 | 0 | err = NDR_ERR_SUCCESS; |
833 | 0 | done: |
834 | 0 | TALLOC_FREE(list_ctx); |
835 | 0 | return err; |
836 | 0 | } |
837 | | |
838 | | NTSTATUS smbd_do_query_getinfo_quota(TALLOC_CTX *mem_ctx, |
839 | | files_struct *fsp, |
840 | | bool restart_scan, |
841 | | bool return_single, |
842 | | uint32_t sid_list_length, |
843 | | DATA_BLOB *sid_buf, |
844 | | uint32_t max_data_count, |
845 | | uint8_t **p_data, |
846 | | uint32_t *p_data_size) |
847 | 0 | { |
848 | 0 | NTSTATUS status; |
849 | 0 | SMB_NTQUOTA_HANDLE *qt_handle = NULL; |
850 | 0 | SMB_NTQUOTA_LIST *qt_list = NULL; |
851 | 0 | DATA_BLOB blob = data_blob_null; |
852 | 0 | enum ndr_err_code err; |
853 | |
|
854 | 0 | qt_handle = |
855 | 0 | (SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->private_data; |
856 | |
|
857 | 0 | if (sid_list_length ) { |
858 | 0 | struct dom_sid *sids; |
859 | 0 | uint32_t elems = 0; |
860 | | /* |
861 | | * error check pulled offsets and lengths for wrap and |
862 | | * exceeding available bytes. |
863 | | */ |
864 | 0 | if (sid_list_length > sid_buf->length) { |
865 | 0 | DBG_ERR("sid_list_length 0x%x exceeds " |
866 | 0 | "available bytes %zx\n", |
867 | 0 | sid_list_length, |
868 | 0 | sid_buf->length); |
869 | 0 | return NT_STATUS_INVALID_PARAMETER; |
870 | 0 | } |
871 | | |
872 | 0 | err = extract_sids_from_buf(mem_ctx, sid_list_length, |
873 | 0 | sid_buf, &sids, &elems); |
874 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(err) || elems == 0) { |
875 | 0 | return NT_STATUS_INVALID_PARAMETER; |
876 | 0 | } |
877 | 0 | err = fill_qtlist_from_sids(mem_ctx, |
878 | 0 | fsp, |
879 | 0 | qt_handle, |
880 | 0 | sids, |
881 | 0 | elems); |
882 | 0 | if (!NDR_ERR_CODE_IS_SUCCESS(err)) { |
883 | 0 | return NT_STATUS_INVALID_PARAMETER; |
884 | 0 | } |
885 | 0 | } else if (restart_scan) { |
886 | 0 | if (vfs_get_user_ntquota_list(fsp, |
887 | 0 | &(qt_handle->quota_list))!=0) { |
888 | 0 | return NT_STATUS_INTERNAL_ERROR; |
889 | 0 | } |
890 | 0 | } else { |
891 | 0 | if (qt_handle->quota_list!=NULL && |
892 | 0 | qt_handle->tmp_list==NULL) { |
893 | 0 | free_ntquota_list(&(qt_handle->quota_list)); |
894 | 0 | } |
895 | 0 | } |
896 | | |
897 | 0 | if (restart_scan !=0 ) { |
898 | 0 | qt_list = qt_handle->quota_list; |
899 | 0 | } else { |
900 | 0 | qt_list = qt_handle->tmp_list; |
901 | 0 | } |
902 | 0 | status = fill_quota_buffer(mem_ctx, qt_list, |
903 | 0 | return_single != 0, |
904 | 0 | max_data_count, |
905 | 0 | &blob, |
906 | 0 | &qt_handle->tmp_list); |
907 | 0 | if (!NT_STATUS_IS_OK(status)) { |
908 | 0 | return status; |
909 | 0 | } |
910 | 0 | if (blob.length > max_data_count) { |
911 | 0 | return NT_STATUS_BUFFER_TOO_SMALL; |
912 | 0 | } |
913 | | |
914 | 0 | *p_data = blob.data; |
915 | 0 | *p_data_size = blob.length; |
916 | 0 | return NT_STATUS_OK; |
917 | 0 | } |
918 | | #endif /* HAVE_SYS_QUOTAS */ |