smbd: Simplify smbd_smb2_setinfo_send()
[samba.git] / source3 / smbd / smb2_setinfo.c
1 /*
2    Unix SMB/CIFS implementation.
3    Core SMB2 server
4
5    Copyright (C) Stefan Metzmacher 2009
6    Copyright (C) Jeremy Allison 2010
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "../libcli/smb/smb_common.h"
26 #include "trans2.h"
27 #include "../lib/util/tevent_ntstatus.h"
28 #include "../librpc/gen_ndr/open_files.h"
29 #include "source3/lib/dbwrap/dbwrap_watch.h"
30 #include "messages.h"
31 #include "librpc/gen_ndr/ndr_quota.h"
32
33 #undef DBGC_CLASS
34 #define DBGC_CLASS DBGC_SMB2
35
36 static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
37                                                  struct tevent_context *ev,
38                                                  struct smbd_smb2_request *smb2req,
39                                                  struct files_struct *in_fsp,
40                                                  uint8_t in_info_type,
41                                                  uint8_t in_file_info_class,
42                                                  DATA_BLOB in_input_buffer,
43                                                  uint32_t in_additional_information);
44 static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req);
45
46 static void smbd_smb2_request_setinfo_done(struct tevent_req *subreq);
47 NTSTATUS smbd_smb2_request_process_setinfo(struct smbd_smb2_request *req)
48 {
49         struct smbXsrv_connection *xconn = req->xconn;
50         NTSTATUS status;
51         const uint8_t *inbody;
52         uint8_t in_info_type;
53         uint8_t in_file_info_class;
54         uint16_t in_input_buffer_offset;
55         uint32_t in_input_buffer_length;
56         DATA_BLOB in_input_buffer;
57         uint32_t in_additional_information;
58         uint64_t in_file_id_persistent;
59         uint64_t in_file_id_volatile;
60         struct files_struct *in_fsp;
61         struct tevent_req *subreq;
62
63         status = smbd_smb2_request_verify_sizes(req, 0x21);
64         if (!NT_STATUS_IS_OK(status)) {
65                 return smbd_smb2_request_error(req, status);
66         }
67         inbody = SMBD_SMB2_IN_BODY_PTR(req);
68
69         in_info_type                    = CVAL(inbody, 0x02);
70         in_file_info_class              = CVAL(inbody, 0x03);
71         in_input_buffer_length          = IVAL(inbody, 0x04);
72         in_input_buffer_offset          = SVAL(inbody, 0x08);
73         /* 0x0A 2 bytes reserved */
74         in_additional_information       = IVAL(inbody, 0x0C);
75         in_file_id_persistent           = BVAL(inbody, 0x10);
76         in_file_id_volatile             = BVAL(inbody, 0x18);
77
78         if (in_input_buffer_offset == 0 && in_input_buffer_length == 0) {
79                 /* This is ok */
80         } else if (in_input_buffer_offset !=
81                    (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
82                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
83         }
84
85         if (in_input_buffer_length > SMBD_SMB2_IN_DYN_LEN(req)) {
86                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
87         }
88
89         in_input_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
90         in_input_buffer.length = in_input_buffer_length;
91
92         if (in_input_buffer.length > xconn->smb2.server.max_trans) {
93                 DEBUG(2,("smbd_smb2_request_process_setinfo: "
94                          "client ignored max trans: %s: 0x%08X: 0x%08X\n",
95                          __location__, (unsigned)in_input_buffer.length,
96                          (unsigned)xconn->smb2.server.max_trans));
97                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
98         }
99
100         status = smbd_smb2_request_verify_creditcharge(req,
101                                                 in_input_buffer.length);
102         if (!NT_STATUS_IS_OK(status)) {
103                 return smbd_smb2_request_error(req, status);
104         }
105
106         in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
107         if (in_fsp == NULL) {
108                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
109         }
110
111         subreq = smbd_smb2_setinfo_send(req, req->sconn->ev_ctx,
112                                         req, in_fsp,
113                                         in_info_type,
114                                         in_file_info_class,
115                                         in_input_buffer,
116                                         in_additional_information);
117         if (subreq == NULL) {
118                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
119         }
120         tevent_req_set_callback(subreq, smbd_smb2_request_setinfo_done, req);
121
122         return smbd_smb2_request_pending_queue(req, subreq, 500);
123 }
124
125 static void smbd_smb2_request_setinfo_done(struct tevent_req *subreq)
126 {
127         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
128                                         struct smbd_smb2_request);
129         DATA_BLOB outbody;
130         NTSTATUS status;
131         NTSTATUS error; /* transport error */
132
133         status = smbd_smb2_setinfo_recv(subreq);
134         TALLOC_FREE(subreq);
135         if (!NT_STATUS_IS_OK(status)) {
136                 error = smbd_smb2_request_error(req, status);
137                 if (!NT_STATUS_IS_OK(error)) {
138                         smbd_server_connection_terminate(req->xconn,
139                                                          nt_errstr(error));
140                         return;
141                 }
142                 return;
143         }
144
145         outbody = smbd_smb2_generate_outbody(req, 0x02);
146         if (outbody.data == NULL) {
147                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
148                 if (!NT_STATUS_IS_OK(error)) {
149                         smbd_server_connection_terminate(req->xconn,
150                                                          nt_errstr(error));
151                         return;
152                 }
153                 return;
154         }
155
156         SSVAL(outbody.data, 0x00, 0x02);        /* struct size */
157
158         error = smbd_smb2_request_done(req, outbody, NULL);
159         if (!NT_STATUS_IS_OK(error)) {
160                 smbd_server_connection_terminate(req->xconn,
161                                                  nt_errstr(error));
162                 return;
163         }
164 }
165
166 struct defer_rename_state {
167         struct tevent_req *req;
168         struct smbd_smb2_request *smb2req;
169         struct tevent_context *ev;
170         struct files_struct *fsp;
171         char *data;
172         int data_size;
173 };
174
175 static int defer_rename_state_destructor(struct defer_rename_state *rename_state)
176 {
177         SAFE_FREE(rename_state->data);
178         return 0;
179 }
180
181 static void defer_rename_done(struct tevent_req *subreq);
182
183 static struct tevent_req *delay_rename_for_lease_break(struct tevent_req *req,
184                                 struct smbd_smb2_request *smb2req,
185                                 struct tevent_context *ev,
186                                 struct files_struct *fsp,
187                                 struct share_mode_lock *lck,
188                                 char *data,
189                                 int data_size)
190
191 {
192         struct tevent_req *subreq;
193         uint32_t i;
194         struct share_mode_data *d = lck->data;
195         struct defer_rename_state *rename_state;
196         bool delay = false;
197         struct timeval timeout;
198
199         if (fsp->oplock_type != LEASE_OPLOCK) {
200                 return NULL;
201         }
202
203         for (i=0; i<d->num_share_modes; i++) {
204                 struct share_mode_entry *e = &d->share_modes[i];
205                 uint32_t e_lease_type;
206                 uint32_t break_to;
207
208                 if (e->op_type != LEASE_OPLOCK) {
209                         continue;
210                 }
211
212                 e_lease_type = get_lease_type(d, e);
213
214                 if (!(e_lease_type & SMB2_LEASE_HANDLE)) {
215                         continue;
216                 }
217
218                 if (smb2_lease_equal(fsp_client_guid(fsp),
219                                 &fsp->lease->lease.lease_key,
220                                 &e->client_guid,
221                                 &e->lease_key)) {
222                         continue;
223                 }
224
225                 if (share_mode_stale_pid(d, i)) {
226                         continue;
227                 }
228
229                 delay = true;
230                 break_to = (e_lease_type & ~SMB2_LEASE_HANDLE);
231
232                 send_break_message(fsp->conn->sconn->msg_ctx, &fsp->file_id,
233                                    e, break_to);
234         }
235
236         if (!delay) {
237                 return NULL;
238         }
239
240         /* Setup a watch on this record. */
241         rename_state = talloc_zero(req, struct defer_rename_state);
242         if (rename_state == NULL) {
243                 return NULL;
244         }
245
246         rename_state->req = req;
247         rename_state->smb2req = smb2req;
248         rename_state->ev = ev;
249         rename_state->fsp = fsp;
250         rename_state->data = data;
251         rename_state->data_size = data_size;
252
253         talloc_set_destructor(rename_state, defer_rename_state_destructor);
254
255         subreq = dbwrap_watched_watch_send(
256                                 rename_state,
257                                 ev,
258                                 lck->data->record,
259                                 (struct server_id){0});
260
261         if (subreq == NULL) {
262                 exit_server("Could not watch share mode record for rename\n");
263         }
264
265         tevent_req_set_callback(subreq, defer_rename_done, rename_state);
266
267         timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0);
268         if (!tevent_req_set_endtime(subreq,
269                         ev,
270                         timeval_sum(&smb2req->request_time, &timeout))) {
271                 exit_server("Could not set rename timeout\n");
272         }
273
274         return subreq;
275 }
276
277 static void defer_rename_done(struct tevent_req *subreq)
278 {
279         struct defer_rename_state *state = tevent_req_callback_data(
280                 subreq, struct defer_rename_state);
281         NTSTATUS status;
282         struct share_mode_lock *lck;
283         int ret_size = 0;
284         bool ok;
285
286         status = dbwrap_watched_watch_recv(subreq, NULL, NULL);
287         TALLOC_FREE(subreq);
288         if (!NT_STATUS_IS_OK(status)) {
289                 DEBUG(5, ("dbwrap_record_watch_recv returned %s\n",
290                         nt_errstr(status)));
291                 tevent_req_nterror(state->req, status);
292                 return;
293         }
294
295         /*
296          * Make sure we run as the user again
297          */
298         ok = change_to_user(state->smb2req->tcon->compat,
299                             state->smb2req->session->compat->vuid);
300         if (!ok) {
301                 tevent_req_nterror(state->req, NT_STATUS_ACCESS_DENIED);
302                 return;
303         }
304
305         /* Do we still need to wait ? */
306         lck = get_existing_share_mode_lock(state->req, state->fsp->file_id);
307         if (lck == NULL) {
308                 tevent_req_nterror(state->req, NT_STATUS_UNSUCCESSFUL);
309                 return;
310         }
311         subreq = delay_rename_for_lease_break(state->req,
312                                 state->smb2req,
313                                 state->ev,
314                                 state->fsp,
315                                 lck,
316                                 state->data,
317                                 state->data_size);
318         if (subreq) {
319                 /* Yep - keep waiting. */
320                 state->data = NULL;
321                 TALLOC_FREE(state);
322                 TALLOC_FREE(lck);
323                 return;
324         }
325
326         /* Do the rename under the lock. */
327         status = smbd_do_setfilepathinfo(state->fsp->conn,
328                                 state->smb2req->smb1req,
329                                 state,
330                                 SMB2_FILE_RENAME_INFORMATION_INTERNAL,
331                                 state->fsp,
332                                 state->fsp->fsp_name,
333                                 &state->data,
334                                 state->data_size,
335                                 &ret_size);
336
337         TALLOC_FREE(lck);
338         SAFE_FREE(state->data);
339
340         if (!NT_STATUS_IS_OK(status)) {
341                 tevent_req_nterror(state->req, status);
342                 return;
343         }
344
345         tevent_req_done(state->req);
346 }
347
348 struct smbd_smb2_setinfo_state {
349         struct smbd_smb2_request *smb2req;
350 };
351
352 static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
353                                                  struct tevent_context *ev,
354                                                  struct smbd_smb2_request *smb2req,
355                                                  struct files_struct *fsp,
356                                                  uint8_t in_info_type,
357                                                  uint8_t in_file_info_class,
358                                                  DATA_BLOB in_input_buffer,
359                                                  uint32_t in_additional_information)
360 {
361         struct tevent_req *req = NULL;
362         struct smbd_smb2_setinfo_state *state = NULL;
363         struct smb_request *smbreq = NULL;
364         connection_struct *conn = smb2req->tcon->compat;
365         struct share_mode_lock *lck = NULL;
366         NTSTATUS status;
367
368         req = tevent_req_create(mem_ctx, &state,
369                                 struct smbd_smb2_setinfo_state);
370         if (req == NULL) {
371                 return NULL;
372         }
373         state->smb2req = smb2req;
374
375         DEBUG(10,("smbd_smb2_setinfo_send: %s - %s\n",
376                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
377
378         smbreq = smbd_smb2_fake_smb_request(smb2req);
379         if (tevent_req_nomem(smbreq, req)) {
380                 return tevent_req_post(req, ev);
381         }
382
383         if (IS_IPC(conn)) {
384                 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
385                 return tevent_req_post(req, ev);
386         }
387
388         switch (in_info_type) {
389         case SMB2_0_INFO_FILE:
390         {
391                 uint16_t file_info_level;
392                 char *data;
393                 int data_size;
394                 int ret_size = 0;
395
396
397                 file_info_level = in_file_info_class + 1000;
398                 if (file_info_level == SMB_FILE_RENAME_INFORMATION) {
399                         /* SMB2_FILE_RENAME_INFORMATION_INTERNAL == 0xFF00 + in_file_info_class */
400                         file_info_level = SMB2_FILE_RENAME_INFORMATION_INTERNAL;
401                 }
402
403                 if (fsp->fh->fd == -1) {
404                         /*
405                          * This is actually a SETFILEINFO on a directory
406                          * handle (returned from an NT SMB). NT5.0 seems
407                          * to do this call. JRA.
408                          */
409                         if (fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH) {
410                                 /* Always do lstat for UNIX calls. */
411                                 if (SMB_VFS_LSTAT(conn, fsp->fsp_name)) {
412                                         DEBUG(3,("smbd_smb2_setinfo_send: "
413                                                  "SMB_VFS_LSTAT of %s failed "
414                                                  "(%s)\n", fsp_str_dbg(fsp),
415                                                  strerror(errno)));
416                                         status = map_nt_error_from_unix(errno);
417                                         tevent_req_nterror(req, status);
418                                         return tevent_req_post(req, ev);
419                                 }
420                         } else {
421                                 if (SMB_VFS_STAT(conn, fsp->fsp_name) != 0) {
422                                         DEBUG(3,("smbd_smb2_setinfo_send: "
423                                                  "fileinfo of %s failed (%s)\n",
424                                                  fsp_str_dbg(fsp),
425                                                  strerror(errno)));
426                                         status = map_nt_error_from_unix(errno);
427                                         tevent_req_nterror(req, status);
428                                         return tevent_req_post(req, ev);
429                                 }
430                         }
431                 } else if (fsp->print_file) {
432                         /*
433                          * Doing a DELETE_ON_CLOSE should cancel a print job.
434                          */
435                         if ((file_info_level == SMB_SET_FILE_DISPOSITION_INFO)
436                             && in_input_buffer.length >= 1
437                             && CVAL(in_input_buffer.data,0)) {
438                                 fsp->fh->private_options |= NTCREATEX_OPTIONS_PRIVATE_DELETE_ON_CLOSE;
439
440                                 DEBUG(3,("smbd_smb2_setinfo_send: "
441                                          "Cancelling print job (%s)\n",
442                                          fsp_str_dbg(fsp)));
443
444                                 tevent_req_done(req);
445                                 return tevent_req_post(req, ev);
446                         }
447                         tevent_req_nterror(req, NT_STATUS_OBJECT_PATH_INVALID);
448                         return tevent_req_post(req, ev);
449                 } else {
450                         /*
451                          * Original code - this is an open file.
452                          */
453
454                         if (SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st) != 0) {
455                                 DEBUG(3,("smbd_smb2_setinfo_send: fstat "
456                                          "of %s failed (%s)\n",
457                                          fsp_fnum_dbg(fsp),
458                                          strerror(errno)));
459                                 status = map_nt_error_from_unix(errno);
460                                 tevent_req_nterror(req, status);
461                                 return tevent_req_post(req, ev);
462                         }
463                 }
464
465                 data = NULL;
466                 data_size = in_input_buffer.length;
467                 if (data_size > 0) {
468                         data = (char *)SMB_MALLOC_ARRAY(char, data_size);
469                         if (tevent_req_nomem(data, req)) {
470                                 return tevent_req_post(req, ev);
471                         }
472                         memcpy(data, in_input_buffer.data, data_size);
473                 }
474
475                 if (file_info_level == SMB2_FILE_RENAME_INFORMATION_INTERNAL) {
476                         struct tevent_req *subreq;
477
478                         lck = get_existing_share_mode_lock(mem_ctx,
479                                                         fsp->file_id);
480                         if (lck == NULL) {
481                                 SAFE_FREE(data);
482                                 tevent_req_nterror(req,
483                                         NT_STATUS_UNSUCCESSFUL);
484                                 return tevent_req_post(req, ev);
485                         }
486
487                         subreq = delay_rename_for_lease_break(req,
488                                                         smb2req,
489                                                         ev,
490                                                         fsp,
491                                                         lck,
492                                                         data,
493                                                         data_size);
494                         if (subreq) {
495                                 /* Wait for lease break response. */
496
497                                 /* Ensure we can't be closed in flight. */
498                                 if (!aio_add_req_to_fsp(fsp, req)) {
499                                         TALLOC_FREE(lck);
500                                         tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
501                                         return tevent_req_post(req, ev);
502                                 }
503
504                                 TALLOC_FREE(lck);
505                                 return req;
506                         }
507                 }
508
509                 status = smbd_do_setfilepathinfo(conn, smbreq, state,
510                                                  file_info_level,
511                                                  fsp,
512                                                  fsp->fsp_name,
513                                                  &data,
514                                                  data_size,
515                                                  &ret_size);
516                 TALLOC_FREE(lck);
517                 SAFE_FREE(data);
518                 if (!NT_STATUS_IS_OK(status)) {
519                         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
520                                 status = NT_STATUS_INVALID_INFO_CLASS;
521                         }
522                         tevent_req_nterror(req, status);
523                         return tevent_req_post(req, ev);
524                 }
525                 break;
526         }
527
528         case SMB2_0_INFO_FILESYSTEM:
529         {
530                 uint16_t file_info_level = in_file_info_class + 1000;
531
532                 status = smbd_do_setfsinfo(conn, smbreq, state,
533                                         file_info_level,
534                                         fsp,
535                                         &in_input_buffer);
536                 if (!NT_STATUS_IS_OK(status)) {
537                         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
538                                 status = NT_STATUS_INVALID_INFO_CLASS;
539                         }
540                         tevent_req_nterror(req, status);
541                         return tevent_req_post(req, ev);
542                 }
543                 break;
544         }
545
546         case SMB2_0_INFO_SECURITY:
547         {
548                 if (!CAN_WRITE(conn)) {
549                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
550                         return tevent_req_post(req, ev);
551                 }
552
553                 status = set_sd_blob(fsp,
554                                 in_input_buffer.data,
555                                 in_input_buffer.length,
556                                 in_additional_information &
557                                 SMB_SUPPORTED_SECINFO_FLAGS);
558                 if (!NT_STATUS_IS_OK(status)) {
559                         tevent_req_nterror(req, status);
560                         return tevent_req_post(req, ev);
561                 }
562                 break;
563         }
564
565         case SMB2_0_INFO_QUOTA:
566         {
567 #ifdef HAVE_SYS_QUOTAS
568                 struct file_quota_information info = {0};
569                 SMB_NTQUOTA_STRUCT qt = {0};
570                 enum ndr_err_code err;
571                 int ret;
572
573                 if (!fsp->fake_file_handle) {
574                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
575                         return tevent_req_post(req, ev);
576                 }
577                 err = ndr_pull_struct_blob(
578                         &in_input_buffer, state, &info,
579                         (ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
580                 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
581                         tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
582                         return tevent_req_post(req, ev);
583                 }
584
585                 qt.usedspace = info.quota_used;
586
587                 qt.softlim = info.quota_threshold;
588
589                 qt.hardlim = info.quota_limit;
590
591                 qt.sid = info.sid;
592                 ret = vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &qt.sid, &qt);
593                 if (ret !=0 ) {
594                         status = map_nt_error_from_unix(errno);
595                         tevent_req_nterror(req, status);
596                         return tevent_req_post(req, ev);
597                 }
598                 status = NT_STATUS_OK;
599                 break;
600 #else
601                 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
602                 return tevent_req_post(req, ev);
603 #endif
604         }
605         default:
606                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
607                 return tevent_req_post(req, ev);
608         }
609
610         tevent_req_done(req);
611         return tevent_req_post(req, ev);
612 }
613
614 static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req)
615 {
616         NTSTATUS status;
617
618         if (tevent_req_is_nterror(req, &status)) {
619                 tevent_req_received(req);
620                 return status;
621         }
622
623         tevent_req_received(req);
624         return NT_STATUS_OK;
625 }