smbd: RIP user_struct
[amitay/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 struct delay_rename_lease_break_state {
184         struct files_struct *fsp;
185         bool delay;
186 };
187
188 static bool delay_rename_lease_break_fn(
189         struct share_mode_entry *e,
190         void *private_data)
191 {
192         struct delay_rename_lease_break_state *state = private_data;
193         struct files_struct *fsp = state->fsp;
194         uint32_t e_lease_type, break_to;
195         bool ours, stale;
196
197         ours = smb2_lease_equal(fsp_client_guid(fsp),
198                                 &fsp->lease->lease.lease_key,
199                                 &e->client_guid,
200                                 &e->lease_key);
201         if (ours) {
202                 return false;
203         }
204
205         e_lease_type = get_lease_type(e, fsp->file_id);
206
207         if ((e_lease_type & SMB2_LEASE_HANDLE) == 0) {
208                 return false;
209         }
210
211         stale = share_entry_stale_pid(e);
212         if (stale) {
213                 return false;
214         }
215
216         break_to = (e_lease_type & ~SMB2_LEASE_HANDLE);
217
218         send_break_message(
219                 fsp->conn->sconn->msg_ctx, &fsp->file_id, e, break_to);
220
221         return false;
222 }
223
224 static struct tevent_req *delay_rename_for_lease_break(struct tevent_req *req,
225                                 struct smbd_smb2_request *smb2req,
226                                 struct tevent_context *ev,
227                                 struct files_struct *fsp,
228                                 struct share_mode_lock *lck,
229                                 char *data,
230                                 int data_size)
231
232 {
233         struct tevent_req *subreq;
234         struct defer_rename_state *rename_state;
235         struct delay_rename_lease_break_state state = { .fsp = fsp };
236         struct timeval timeout;
237         bool ok;
238
239         if (fsp->oplock_type != LEASE_OPLOCK) {
240                 return NULL;
241         }
242
243         ok = share_mode_forall_leases(
244                 lck, delay_rename_lease_break_fn, &state);
245         if (!ok) {
246                 return NULL;
247         }
248
249         if (!state.delay) {
250                 return NULL;
251         }
252
253         /* Setup a watch on this record. */
254         rename_state = talloc_zero(req, struct defer_rename_state);
255         if (rename_state == NULL) {
256                 return NULL;
257         }
258
259         rename_state->req = req;
260         rename_state->smb2req = smb2req;
261         rename_state->ev = ev;
262         rename_state->fsp = fsp;
263         rename_state->data = data;
264         rename_state->data_size = data_size;
265
266         talloc_set_destructor(rename_state, defer_rename_state_destructor);
267
268         subreq = share_mode_watch_send(
269                                 rename_state,
270                                 ev,
271                                 lck->data->id,
272                                 (struct server_id){0});
273
274         if (subreq == NULL) {
275                 exit_server("Could not watch share mode record for rename\n");
276         }
277
278         tevent_req_set_callback(subreq, defer_rename_done, rename_state);
279
280         timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0);
281         if (!tevent_req_set_endtime(subreq,
282                         ev,
283                         timeval_sum(&smb2req->request_time, &timeout))) {
284                 exit_server("Could not set rename timeout\n");
285         }
286
287         return subreq;
288 }
289
290 static void defer_rename_done(struct tevent_req *subreq)
291 {
292         struct defer_rename_state *state = tevent_req_callback_data(
293                 subreq, struct defer_rename_state);
294         NTSTATUS status;
295         struct share_mode_lock *lck;
296         int ret_size = 0;
297         bool ok;
298
299         status = share_mode_watch_recv(subreq, NULL, NULL);
300         TALLOC_FREE(subreq);
301         if (!NT_STATUS_IS_OK(status)) {
302                 DEBUG(5, ("dbwrap_record_watch_recv returned %s\n",
303                         nt_errstr(status)));
304                 tevent_req_nterror(state->req, status);
305                 return;
306         }
307
308         /*
309          * Make sure we run as the user again
310          */
311         ok = change_to_user_and_service(
312                 state->smb2req->tcon->compat,
313                 state->smb2req->session->global->session_wire_id);
314         if (!ok) {
315                 tevent_req_nterror(state->req, NT_STATUS_ACCESS_DENIED);
316                 return;
317         }
318
319         /* Do we still need to wait ? */
320         lck = get_existing_share_mode_lock(state->req, state->fsp->file_id);
321         if (lck == NULL) {
322                 tevent_req_nterror(state->req, NT_STATUS_UNSUCCESSFUL);
323                 return;
324         }
325         subreq = delay_rename_for_lease_break(state->req,
326                                 state->smb2req,
327                                 state->ev,
328                                 state->fsp,
329                                 lck,
330                                 state->data,
331                                 state->data_size);
332         if (subreq) {
333                 /* Yep - keep waiting. */
334                 state->data = NULL;
335                 TALLOC_FREE(state);
336                 TALLOC_FREE(lck);
337                 return;
338         }
339
340         /* Do the rename under the lock. */
341         status = smbd_do_setfilepathinfo(state->fsp->conn,
342                                 state->smb2req->smb1req,
343                                 state,
344                                 SMB2_FILE_RENAME_INFORMATION_INTERNAL,
345                                 state->fsp,
346                                 state->fsp->fsp_name,
347                                 &state->data,
348                                 state->data_size,
349                                 &ret_size);
350
351         TALLOC_FREE(lck);
352         SAFE_FREE(state->data);
353
354         if (!NT_STATUS_IS_OK(status)) {
355                 tevent_req_nterror(state->req, status);
356                 return;
357         }
358
359         tevent_req_done(state->req);
360 }
361
362 struct smbd_smb2_setinfo_state {
363         struct smbd_smb2_request *smb2req;
364 };
365
366 static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
367                                                  struct tevent_context *ev,
368                                                  struct smbd_smb2_request *smb2req,
369                                                  struct files_struct *fsp,
370                                                  uint8_t in_info_type,
371                                                  uint8_t in_file_info_class,
372                                                  DATA_BLOB in_input_buffer,
373                                                  uint32_t in_additional_information)
374 {
375         struct tevent_req *req = NULL;
376         struct smbd_smb2_setinfo_state *state = NULL;
377         struct smb_request *smbreq = NULL;
378         connection_struct *conn = smb2req->tcon->compat;
379         struct share_mode_lock *lck = NULL;
380         NTSTATUS status;
381
382         req = tevent_req_create(mem_ctx, &state,
383                                 struct smbd_smb2_setinfo_state);
384         if (req == NULL) {
385                 return NULL;
386         }
387         state->smb2req = smb2req;
388
389         DEBUG(10,("smbd_smb2_setinfo_send: %s - %s\n",
390                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
391
392         smbreq = smbd_smb2_fake_smb_request(smb2req);
393         if (tevent_req_nomem(smbreq, req)) {
394                 return tevent_req_post(req, ev);
395         }
396
397         if (IS_IPC(conn)) {
398                 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
399                 return tevent_req_post(req, ev);
400         }
401
402         switch (in_info_type) {
403         case SMB2_0_INFO_FILE:
404         {
405                 uint16_t file_info_level;
406                 char *data;
407                 int data_size;
408                 int ret_size = 0;
409
410
411                 file_info_level = in_file_info_class + 1000;
412                 if (file_info_level == SMB_FILE_RENAME_INFORMATION) {
413                         /* SMB2_FILE_RENAME_INFORMATION_INTERNAL == 0xFF00 + in_file_info_class */
414                         file_info_level = SMB2_FILE_RENAME_INFORMATION_INTERNAL;
415                 }
416
417                 if (fsp->fh->fd == -1) {
418                         /*
419                          * This is actually a SETFILEINFO on a directory
420                          * handle (returned from an NT SMB). NT5.0 seems
421                          * to do this call. JRA.
422                          */
423                         if (fsp->fsp_name->flags & SMB_FILENAME_POSIX_PATH) {
424                                 /* Always do lstat for UNIX calls. */
425                                 if (SMB_VFS_LSTAT(conn, fsp->fsp_name)) {
426                                         DEBUG(3,("smbd_smb2_setinfo_send: "
427                                                  "SMB_VFS_LSTAT of %s failed "
428                                                  "(%s)\n", fsp_str_dbg(fsp),
429                                                  strerror(errno)));
430                                         status = map_nt_error_from_unix(errno);
431                                         tevent_req_nterror(req, status);
432                                         return tevent_req_post(req, ev);
433                                 }
434                         } else {
435                                 if (SMB_VFS_STAT(conn, fsp->fsp_name) != 0) {
436                                         DEBUG(3,("smbd_smb2_setinfo_send: "
437                                                  "fileinfo of %s failed (%s)\n",
438                                                  fsp_str_dbg(fsp),
439                                                  strerror(errno)));
440                                         status = map_nt_error_from_unix(errno);
441                                         tevent_req_nterror(req, status);
442                                         return tevent_req_post(req, ev);
443                                 }
444                         }
445                 } else if (fsp->print_file) {
446                         /*
447                          * Doing a DELETE_ON_CLOSE should cancel a print job.
448                          */
449                         if ((file_info_level == SMB_SET_FILE_DISPOSITION_INFO)
450                             && in_input_buffer.length >= 1
451                             && CVAL(in_input_buffer.data,0)) {
452                                 fsp->fh->private_options |= NTCREATEX_OPTIONS_PRIVATE_DELETE_ON_CLOSE;
453
454                                 DEBUG(3,("smbd_smb2_setinfo_send: "
455                                          "Cancelling print job (%s)\n",
456                                          fsp_str_dbg(fsp)));
457
458                                 tevent_req_done(req);
459                                 return tevent_req_post(req, ev);
460                         }
461                         tevent_req_nterror(req, NT_STATUS_OBJECT_PATH_INVALID);
462                         return tevent_req_post(req, ev);
463                 } else {
464                         /*
465                          * Original code - this is an open file.
466                          */
467
468                         if (SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st) != 0) {
469                                 DEBUG(3,("smbd_smb2_setinfo_send: fstat "
470                                          "of %s failed (%s)\n",
471                                          fsp_fnum_dbg(fsp),
472                                          strerror(errno)));
473                                 status = map_nt_error_from_unix(errno);
474                                 tevent_req_nterror(req, status);
475                                 return tevent_req_post(req, ev);
476                         }
477                 }
478
479                 data = NULL;
480                 data_size = in_input_buffer.length;
481                 if (data_size > 0) {
482                         data = (char *)SMB_MALLOC_ARRAY(char, data_size);
483                         if (tevent_req_nomem(data, req)) {
484                                 return tevent_req_post(req, ev);
485                         }
486                         memcpy(data, in_input_buffer.data, data_size);
487                 }
488
489                 if (file_info_level == SMB2_FILE_RENAME_INFORMATION_INTERNAL) {
490                         struct tevent_req *subreq;
491
492                         lck = get_existing_share_mode_lock(mem_ctx,
493                                                         fsp->file_id);
494                         if (lck == NULL) {
495                                 SAFE_FREE(data);
496                                 tevent_req_nterror(req,
497                                         NT_STATUS_UNSUCCESSFUL);
498                                 return tevent_req_post(req, ev);
499                         }
500
501                         subreq = delay_rename_for_lease_break(req,
502                                                         smb2req,
503                                                         ev,
504                                                         fsp,
505                                                         lck,
506                                                         data,
507                                                         data_size);
508                         if (subreq) {
509                                 /* Wait for lease break response. */
510
511                                 /* Ensure we can't be closed in flight. */
512                                 if (!aio_add_req_to_fsp(fsp, req)) {
513                                         TALLOC_FREE(lck);
514                                         tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
515                                         return tevent_req_post(req, ev);
516                                 }
517
518                                 TALLOC_FREE(lck);
519                                 return req;
520                         }
521                 }
522
523                 status = smbd_do_setfilepathinfo(conn, smbreq, state,
524                                                  file_info_level,
525                                                  fsp,
526                                                  fsp->fsp_name,
527                                                  &data,
528                                                  data_size,
529                                                  &ret_size);
530                 TALLOC_FREE(lck);
531                 SAFE_FREE(data);
532                 if (!NT_STATUS_IS_OK(status)) {
533                         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
534                                 status = NT_STATUS_INVALID_INFO_CLASS;
535                         }
536                         tevent_req_nterror(req, status);
537                         return tevent_req_post(req, ev);
538                 }
539                 break;
540         }
541
542         case SMB2_0_INFO_FILESYSTEM:
543         {
544                 uint16_t file_info_level = in_file_info_class + 1000;
545
546                 status = smbd_do_setfsinfo(conn, smbreq, state,
547                                         file_info_level,
548                                         fsp,
549                                         &in_input_buffer);
550                 if (!NT_STATUS_IS_OK(status)) {
551                         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL)) {
552                                 status = NT_STATUS_INVALID_INFO_CLASS;
553                         }
554                         tevent_req_nterror(req, status);
555                         return tevent_req_post(req, ev);
556                 }
557                 break;
558         }
559
560         case SMB2_0_INFO_SECURITY:
561         {
562                 if (!CAN_WRITE(conn)) {
563                         tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
564                         return tevent_req_post(req, ev);
565                 }
566
567                 status = set_sd_blob(fsp,
568                                 in_input_buffer.data,
569                                 in_input_buffer.length,
570                                 in_additional_information &
571                                 SMB_SUPPORTED_SECINFO_FLAGS);
572                 if (!NT_STATUS_IS_OK(status)) {
573                         tevent_req_nterror(req, status);
574                         return tevent_req_post(req, ev);
575                 }
576                 break;
577         }
578
579         case SMB2_0_INFO_QUOTA:
580         {
581 #ifdef HAVE_SYS_QUOTAS
582                 struct file_quota_information info = {0};
583                 SMB_NTQUOTA_STRUCT qt = {0};
584                 enum ndr_err_code err;
585                 int ret;
586
587                 if (!fsp->fake_file_handle) {
588                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
589                         return tevent_req_post(req, ev);
590                 }
591                 err = ndr_pull_struct_blob(
592                         &in_input_buffer, state, &info,
593                         (ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
594                 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
595                         tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
596                         return tevent_req_post(req, ev);
597                 }
598
599                 qt.usedspace = info.quota_used;
600
601                 qt.softlim = info.quota_threshold;
602
603                 qt.hardlim = info.quota_limit;
604
605                 qt.sid = info.sid;
606                 ret = vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &qt.sid, &qt);
607                 if (ret !=0 ) {
608                         status = map_nt_error_from_unix(errno);
609                         tevent_req_nterror(req, status);
610                         return tevent_req_post(req, ev);
611                 }
612                 status = NT_STATUS_OK;
613                 break;
614 #else
615                 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
616                 return tevent_req_post(req, ev);
617 #endif
618         }
619         default:
620                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
621                 return tevent_req_post(req, ev);
622         }
623
624         tevent_req_done(req);
625         return tevent_req_post(req, ev);
626 }
627
628 static NTSTATUS smbd_smb2_setinfo_recv(struct tevent_req *req)
629 {
630         NTSTATUS status;
631
632         if (tevent_req_is_nterror(req, &status)) {
633                 tevent_req_received(req);
634                 return status;
635         }
636
637         tevent_req_received(req);
638         return NT_STATUS_OK;
639 }