libcli: Make symlink_reparse_buffer_parse() more flexible
[samba.git] / source3 / libsmb / clisymlink.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Client implementation of setting symlinks using reparse points
4  * Copyright (C) Volker Lendecke 2011
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 "system/filesys.h"
22 #include "libsmb/libsmb.h"
23 #include "../lib/util/tevent_ntstatus.h"
24 #include "async_smb.h"
25 #include "libsmb/clirap.h"
26 #include "trans2.h"
27 #include "libcli/security/secdesc.h"
28 #include "libcli/security/security.h"
29 #include "../libcli/smb/smbXcli_base.h"
30 #include "libcli/smb/reparse_symlink.h"
31
32 struct cli_symlink_state {
33         struct tevent_context *ev;
34         struct cli_state *cli;
35         const char *link_target;
36         const char *newpath;
37         uint32_t flags;
38
39         uint16_t fnum;
40         DATA_BLOB in;
41
42         NTSTATUS set_reparse_status;
43 };
44
45 static void cli_symlink_create_done(struct tevent_req *subreq);
46 static void cli_symlink_set_reparse_done(struct tevent_req *subreq);
47 static void cli_symlink_delete_on_close_done(struct tevent_req *subreq);
48 static void cli_symlink_close_done(struct tevent_req *subreq);
49
50 struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx,
51                                     struct tevent_context *ev,
52                                     struct cli_state *cli,
53                                     const char *link_target,
54                                     const char *newpath,
55                                     uint32_t flags)
56 {
57         struct tevent_req *req, *subreq;
58         struct cli_symlink_state *state;
59
60         req = tevent_req_create(mem_ctx, &state, struct cli_symlink_state);
61         if (req == NULL) {
62                 return NULL;
63         }
64         state->ev = ev;
65         state->cli = cli;
66         state->link_target = link_target;
67         state->newpath = newpath;
68         state->flags = flags;
69
70         subreq = cli_ntcreate_send(
71                 state, ev, cli, state->newpath, 0,
72                 SYNCHRONIZE_ACCESS|DELETE_ACCESS|
73                 FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES,
74                 FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE, FILE_CREATE,
75                 FILE_OPEN_REPARSE_POINT|FILE_SYNCHRONOUS_IO_NONALERT|
76                 FILE_NON_DIRECTORY_FILE,
77                 SMB2_IMPERSONATION_IMPERSONATION, 0);
78         if (tevent_req_nomem(subreq, req)) {
79                 return tevent_req_post(req, ev);
80         }
81         tevent_req_set_callback(subreq, cli_symlink_create_done, req);
82         return req;
83 }
84
85 static void cli_symlink_create_done(struct tevent_req *subreq)
86 {
87         struct tevent_req *req = tevent_req_callback_data(
88                 subreq, struct tevent_req);
89         struct cli_symlink_state *state = tevent_req_data(
90                 req, struct cli_symlink_state);
91         NTSTATUS status;
92
93         status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
94         TALLOC_FREE(subreq);
95         if (tevent_req_nterror(req, status)) {
96                 return;
97         }
98
99         if (!symlink_reparse_buffer_marshall(
100                     state->link_target, NULL, 0, state->flags, state,
101                     &state->in.data, &state->in.length)) {
102                 tevent_req_oom(req);
103                 return;
104         }
105
106         subreq = cli_fsctl_send(
107                 state,
108                 state->ev,
109                 state->cli,
110                 state->fnum,
111                 FSCTL_SET_REPARSE_POINT,
112                 &state->in,
113                 0);
114         if (tevent_req_nomem(subreq, req)) {
115                 return;
116         }
117         tevent_req_set_callback(subreq, cli_symlink_set_reparse_done, req);
118 }
119
120 static void cli_symlink_set_reparse_done(struct tevent_req *subreq)
121 {
122         struct tevent_req *req = tevent_req_callback_data(
123                 subreq, struct tevent_req);
124         struct cli_symlink_state *state = tevent_req_data(
125                 req, struct cli_symlink_state);
126
127         state->set_reparse_status = cli_fsctl_recv(subreq, NULL, NULL);
128         TALLOC_FREE(subreq);
129
130         if (NT_STATUS_IS_OK(state->set_reparse_status)) {
131                 subreq = cli_close_send(state, state->ev, state->cli,
132                                         state->fnum);
133                 if (tevent_req_nomem(subreq, req)) {
134                         return;
135                 }
136                 tevent_req_set_callback(subreq, cli_symlink_close_done, req);
137                 return;
138         }
139         subreq = cli_nt_delete_on_close_send(
140                 state, state->ev, state->cli, state->fnum, true);
141         if (tevent_req_nomem(subreq, req)) {
142                 return;
143         }
144         tevent_req_set_callback(subreq, cli_symlink_delete_on_close_done, req);
145 }
146
147 static void cli_symlink_delete_on_close_done(struct tevent_req *subreq)
148 {
149         struct tevent_req *req = tevent_req_callback_data(
150                 subreq, struct tevent_req);
151         struct cli_symlink_state *state = tevent_req_data(
152                 req, struct cli_symlink_state);
153
154         /*
155          * Ignore status, we can't do much anyway in case of failure
156          */
157
158         (void)cli_nt_delete_on_close_recv(subreq);
159         TALLOC_FREE(subreq);
160
161         subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
162         if (tevent_req_nomem(subreq, req)) {
163                 return;
164         }
165         tevent_req_set_callback(subreq, cli_symlink_close_done, req);
166 }
167
168 static void cli_symlink_close_done(struct tevent_req *subreq)
169 {
170         struct tevent_req *req = tevent_req_callback_data(
171                 subreq, struct tevent_req);
172         struct cli_symlink_state *state = tevent_req_data(
173                 req, struct cli_symlink_state);
174         NTSTATUS status;
175
176         status = cli_close_recv(subreq);
177         TALLOC_FREE(subreq);
178
179         if (tevent_req_nterror(req, status)) {
180                 return;
181         }
182         if (tevent_req_nterror(req, state->set_reparse_status)) {
183                 return;
184         }
185         tevent_req_done(req);
186 }
187
188 NTSTATUS cli_symlink_recv(struct tevent_req *req)
189 {
190         return tevent_req_simple_recv_ntstatus(req);
191 }
192
193 NTSTATUS cli_symlink(struct cli_state *cli, const char *link_target,
194                      const char *newname, uint32_t flags)
195 {
196         TALLOC_CTX *frame = talloc_stackframe();
197         struct tevent_context *ev;
198         struct tevent_req *req;
199         NTSTATUS status = NT_STATUS_NO_MEMORY;
200
201         if (smbXcli_conn_has_async_calls(cli->conn)) {
202                 status = NT_STATUS_INVALID_PARAMETER;
203                 goto fail;
204         }
205         ev = samba_tevent_context_init(frame);
206         if (ev == NULL) {
207                 goto fail;
208         }
209         req = cli_symlink_send(frame, ev, cli, link_target, newname, flags);
210         if (req == NULL) {
211                 goto fail;
212         }
213         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
214                 goto fail;
215         }
216         status = cli_symlink_recv(req);
217  fail:
218         TALLOC_FREE(frame);
219         return status;
220 }
221
222 struct cli_readlink_state {
223         struct tevent_context *ev;
224         struct cli_state *cli;
225         uint16_t fnum;
226
227         uint16_t setup[4];
228         NTSTATUS get_reparse_status;
229         uint8_t *data;
230         uint32_t num_data;
231         char *target;
232 };
233
234 static void cli_readlink_posix1_done(struct tevent_req *subreq);
235 static void cli_readlink_opened(struct tevent_req *subreq);
236 static void cli_readlink_got_reparse_data(struct tevent_req *subreq);
237 static void cli_readlink_closed(struct tevent_req *subreq);
238
239 struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx,
240                                      struct tevent_context *ev,
241                                      struct cli_state *cli,
242                                      const char *fname)
243 {
244         struct tevent_req *req, *subreq;
245         struct cli_readlink_state *state;
246
247         req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state);
248         if (req == NULL) {
249                 return NULL;
250         }
251         state->ev = ev;
252         state->cli = cli;
253
254         if (cli->requested_posix_capabilities != 0) {
255                 /*
256                  * Only happens for negotiated SMB1 posix caps
257                  */
258                 subreq = cli_posix_readlink_send(state, ev, cli, fname);
259                 if (tevent_req_nomem(subreq, req)) {
260                         return tevent_req_post(req, ev);
261                 }
262                 tevent_req_set_callback(subreq, cli_readlink_posix1_done, req);
263                 return req;
264         }
265
266         subreq = cli_ntcreate_send(
267                 state, ev, cli, fname, 0, FILE_READ_ATTRIBUTES | FILE_READ_EA,
268                 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
269                 FILE_OPEN, FILE_OPEN_REPARSE_POINT,
270                 SMB2_IMPERSONATION_IMPERSONATION, 0);
271         if (tevent_req_nomem(subreq, req)) {
272                 return tevent_req_post(req, ev);
273         }
274         tevent_req_set_callback(subreq, cli_readlink_opened, req);
275         return req;
276 }
277
278 static void cli_readlink_posix1_done(struct tevent_req *subreq)
279 {
280         struct tevent_req *req = tevent_req_callback_data(
281                 subreq, struct tevent_req);
282         struct cli_readlink_state *state = tevent_req_data(
283                 req, struct cli_readlink_state);
284         NTSTATUS status;
285
286         status = cli_posix_readlink_recv(subreq, state, &state->target);
287         TALLOC_FREE(subreq);
288         if (tevent_req_nterror(req, status)) {
289                 return;
290         }
291         tevent_req_done(req);
292 }
293
294 static void cli_readlink_opened(struct tevent_req *subreq)
295 {
296         struct tevent_req *req = tevent_req_callback_data(
297                 subreq, struct tevent_req);
298         struct cli_readlink_state *state = tevent_req_data(
299                 req, struct cli_readlink_state);
300         NTSTATUS status;
301
302         status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
303         TALLOC_FREE(subreq);
304         if (tevent_req_nterror(req, status)) {
305                 return;
306         }
307
308         subreq = cli_fsctl_send(
309                 state,
310                 state->ev,
311                 state->cli,
312                 state->fnum,
313                 FSCTL_GET_REPARSE_POINT,
314                 NULL,
315                 65536);
316
317         if (tevent_req_nomem(subreq, req)) {
318                 return;
319         }
320         tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req);
321 }
322
323 static void cli_readlink_got_reparse_data(struct tevent_req *subreq)
324 {
325         struct tevent_req *req = tevent_req_callback_data(
326                 subreq, struct tevent_req);
327         struct cli_readlink_state *state = tevent_req_data(
328                 req, struct cli_readlink_state);
329         DATA_BLOB out = { .data = NULL, };
330
331         state->get_reparse_status = cli_fsctl_recv(subreq, state, &out);
332         TALLOC_FREE(subreq);
333
334         if (NT_STATUS_IS_OK(state->get_reparse_status)) {
335                 state->data = out.data;
336                 state->num_data = out.length;
337         }
338
339         subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
340         if (tevent_req_nomem(subreq, req)) {
341                 return;
342         }
343         tevent_req_set_callback(subreq, cli_readlink_closed, req);
344 }
345
346 static void cli_readlink_closed(struct tevent_req *subreq)
347 {
348         struct tevent_req *req = tevent_req_callback_data(
349                 subreq, struct tevent_req);
350         struct cli_readlink_state *state = tevent_req_data(
351                 req, struct cli_readlink_state);
352         NTSTATUS status;
353
354         status = cli_close_recv(subreq);
355         TALLOC_FREE(subreq);
356         if (tevent_req_nterror(req, status)) {
357                 return;
358         }
359         if (tevent_req_nterror(req, state->get_reparse_status)) {
360                 return;
361         }
362         tevent_req_done(req);
363 }
364
365 NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
366                            char **psubstitute_name, char **pprint_name,
367                            uint32_t *pflags)
368 {
369         struct cli_readlink_state *state = tevent_req_data(
370                 req, struct cli_readlink_state);
371         struct symlink_reparse_struct symlink = { .flags = 0, };
372         NTSTATUS status;
373         int ret;
374
375         if (tevent_req_is_nterror(req, &status)) {
376                 return status;
377         }
378
379         if (state->target != NULL) {
380                 /*
381                  * SMB1 posix version
382                  */
383                 if (psubstitute_name != NULL) {
384                         *psubstitute_name = talloc_move(
385                                 mem_ctx, &state->target);
386                 }
387                 if (pprint_name != NULL) {
388                         *pprint_name = NULL;
389                 }
390                 if (pflags != NULL) {
391                         *pflags = 0;
392                 }
393                 return NT_STATUS_OK;
394         }
395
396         ret = symlink_reparse_buffer_parse(
397                 talloc_tos(), &symlink, state->data, state->num_data);
398         if (ret != 0) {
399                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
400         }
401
402         if (psubstitute_name != NULL) {
403                 *psubstitute_name = talloc_move(
404                         mem_ctx, &symlink.substitute_name);
405         }
406
407         if (pprint_name != NULL) {
408                 *pprint_name = talloc_move(mem_ctx, &symlink.print_name);
409         }
410
411         if (pflags != NULL) {
412                 *pflags = symlink.flags;
413         }
414
415         TALLOC_FREE(symlink.print_name);
416         TALLOC_FREE(symlink.substitute_name);
417
418         return NT_STATUS_OK;
419 }
420
421 NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
422                        TALLOC_CTX *mem_ctx, char **psubstitute_name,
423                       char **pprint_name, uint32_t *pflags)
424 {
425         TALLOC_CTX *frame = talloc_stackframe();
426         struct tevent_context *ev;
427         struct tevent_req *req;
428         NTSTATUS status = NT_STATUS_NO_MEMORY;
429
430         if (smbXcli_conn_has_async_calls(cli->conn)) {
431                 status = NT_STATUS_INVALID_PARAMETER;
432                 goto fail;
433         }
434         ev = samba_tevent_context_init(frame);
435         if (ev == NULL) {
436                 goto fail;
437         }
438         req = cli_readlink_send(frame, ev, cli, fname);
439         if (req == NULL) {
440                 goto fail;
441         }
442         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
443                 goto fail;
444         }
445         status = cli_readlink_recv(req, mem_ctx, psubstitute_name,
446                                    pprint_name, pflags);
447  fail:
448         TALLOC_FREE(frame);
449         return status;
450 }