Use python.h from libreplace
[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.h"
31
32 struct cli_create_reparse_point_state {
33         struct tevent_context *ev;
34         struct cli_state *cli;
35         DATA_BLOB reparse_blob;
36         uint16_t fnum;
37         NTSTATUS set_reparse_status;
38 };
39
40 static void cli_create_reparse_point_opened(struct tevent_req *subreq);
41 static void cli_create_reparse_point_done(struct tevent_req *subreq);
42 static void cli_create_reparse_point_doc_done(struct tevent_req *subreq);
43 static void cli_create_reparse_point_closed(struct tevent_req *subreq);
44
45 struct tevent_req *cli_create_reparse_point_send(TALLOC_CTX *mem_ctx,
46                                                  struct tevent_context *ev,
47                                                  struct cli_state *cli,
48                                                  const char *fname,
49                                                  DATA_BLOB reparse_blob)
50 {
51         struct tevent_req *req = NULL, *subreq = NULL;
52         struct cli_create_reparse_point_state *state = NULL;
53
54         req = tevent_req_create(mem_ctx,
55                                 &state,
56                                 struct cli_create_reparse_point_state);
57         if (req == NULL) {
58                 return NULL;
59         }
60         state->ev = ev;
61         state->cli = cli;
62         state->reparse_blob = reparse_blob;
63
64         /*
65          * The create arguments were taken from a Windows->Windows
66          * symlink create call.
67          */
68         subreq = cli_ntcreate_send(
69                 state,
70                 ev,
71                 cli,
72                 fname,
73                 0,
74                 SYNCHRONIZE_ACCESS | DELETE_ACCESS | FILE_READ_ATTRIBUTES |
75                         FILE_WRITE_ATTRIBUTES,
76                 FILE_ATTRIBUTE_NORMAL,
77                 FILE_SHARE_NONE,
78                 FILE_CREATE,
79                 FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT |
80                         FILE_NON_DIRECTORY_FILE,
81                 SMB2_IMPERSONATION_IMPERSONATION,
82                 0);
83         if (tevent_req_nomem(subreq, req)) {
84                 return tevent_req_post(req, ev);
85         }
86         tevent_req_set_callback(subreq, cli_create_reparse_point_opened, req);
87         return req;
88 }
89
90 static void cli_create_reparse_point_opened(struct tevent_req *subreq)
91 {
92         struct tevent_req *req =
93                 tevent_req_callback_data(subreq, struct tevent_req);
94         struct cli_create_reparse_point_state *state =
95                 tevent_req_data(req, struct cli_create_reparse_point_state);
96         NTSTATUS status;
97
98         status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
99         TALLOC_FREE(subreq);
100         if (tevent_req_nterror(req, status)) {
101                 return;
102         }
103
104         subreq = cli_fsctl_send(state,
105                                 state->ev,
106                                 state->cli,
107                                 state->fnum,
108                                 FSCTL_SET_REPARSE_POINT,
109                                 &state->reparse_blob,
110                                 0);
111         if (tevent_req_nomem(subreq, req)) {
112                 return;
113         }
114         tevent_req_set_callback(subreq, cli_create_reparse_point_done, req);
115 }
116
117 static void cli_create_reparse_point_done(struct tevent_req *subreq)
118 {
119         struct tevent_req *req = tevent_req_callback_data(
120                 subreq, struct tevent_req);
121         struct cli_create_reparse_point_state *state =
122                 tevent_req_data(req, struct cli_create_reparse_point_state);
123
124         state->set_reparse_status = cli_fsctl_recv(subreq, NULL, NULL);
125         TALLOC_FREE(subreq);
126
127         if (NT_STATUS_IS_OK(state->set_reparse_status)) {
128                 subreq = cli_close_send(state,
129                                         state->ev,
130                                         state->cli,
131                                         state->fnum,
132                                         0);
133                 if (tevent_req_nomem(subreq, req)) {
134                         return;
135                 }
136                 tevent_req_set_callback(subreq,
137                                         cli_create_reparse_point_closed,
138                                         req);
139                 return;
140         }
141         subreq = cli_nt_delete_on_close_send(
142                 state, state->ev, state->cli, state->fnum, true);
143         if (tevent_req_nomem(subreq, req)) {
144                 return;
145         }
146         tevent_req_set_callback(subreq,
147                                 cli_create_reparse_point_doc_done,
148                                 req);
149 }
150
151 static void cli_create_reparse_point_doc_done(struct tevent_req *subreq)
152 {
153         struct tevent_req *req = tevent_req_callback_data(
154                 subreq, struct tevent_req);
155         struct cli_create_reparse_point_state *state =
156                 tevent_req_data(req, struct cli_create_reparse_point_state);
157
158         /*
159          * Ignore status, we can't do much anyway in case of failure
160          */
161
162         (void)cli_nt_delete_on_close_recv(subreq);
163         TALLOC_FREE(subreq);
164
165         subreq = cli_close_send(state, state->ev, state->cli, state->fnum, 0);
166         if (tevent_req_nomem(subreq, req)) {
167                 return;
168         }
169         tevent_req_set_callback(subreq, cli_create_reparse_point_closed, req);
170 }
171
172 static void cli_create_reparse_point_closed(struct tevent_req *subreq)
173 {
174         struct tevent_req *req = tevent_req_callback_data(
175                 subreq, struct tevent_req);
176         struct cli_create_reparse_point_state *state =
177                 tevent_req_data(req, struct cli_create_reparse_point_state);
178         NTSTATUS status;
179
180         status = cli_close_recv(subreq);
181         TALLOC_FREE(subreq);
182
183         if (tevent_req_nterror(req, status)) {
184                 return;
185         }
186         if (tevent_req_nterror(req, state->set_reparse_status)) {
187                 return;
188         }
189         tevent_req_done(req);
190 }
191
192 NTSTATUS cli_create_reparse_point_recv(struct tevent_req *req)
193 {
194         return tevent_req_simple_recv_ntstatus(req);
195 }
196
197 struct cli_symlink_state {
198         uint8_t dummy;
199 };
200
201 static void cli_symlink_done(struct tevent_req *subreq);
202
203 struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx,
204                                     struct tevent_context *ev,
205                                     struct cli_state *cli,
206                                     const char *link_target,
207                                     const char *newpath,
208                                     uint32_t flags)
209 {
210         struct tevent_req *req = NULL, *subreq = NULL;
211         struct cli_symlink_state *state = NULL;
212         struct reparse_data_buffer reparse_buf = {
213                 .tag = IO_REPARSE_TAG_SYMLINK,
214                 .parsed.lnk.substitute_name =
215                         discard_const_p(char, link_target),
216                 .parsed.lnk.print_name = discard_const_p(char, link_target),
217                 .parsed.lnk.flags = flags,
218         };
219         uint8_t *buf;
220         ssize_t buflen;
221
222         req = tevent_req_create(mem_ctx, &state, struct cli_symlink_state);
223         if (req == NULL) {
224                 return NULL;
225         }
226
227         buflen = reparse_data_buffer_marshall(&reparse_buf, NULL, 0);
228         if (buflen == -1) {
229                 tevent_req_oom(req);
230                 return tevent_req_post(req, ev);
231         }
232
233         buf = talloc_array(state, uint8_t, buflen);
234         if (tevent_req_nomem(buf, req)) {
235                 return tevent_req_post(req, ev);
236         }
237
238         buflen = reparse_data_buffer_marshall(&reparse_buf, buf, buflen);
239         if (buflen != talloc_array_length(buf)) {
240                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
241                 return tevent_req_post(req, ev);
242         }
243
244         subreq = cli_create_reparse_point_send(state,
245                                                ev,
246                                                cli,
247                                                newpath,
248                                                (DATA_BLOB){
249                                                        .data = buf,
250                                                        .length = buflen,
251                                                });
252         if (tevent_req_nomem(subreq, req)) {
253                 return tevent_req_post(req, ev);
254         }
255         tevent_req_set_callback(subreq, cli_symlink_done, req);
256         return req;
257 }
258
259 static void cli_symlink_done(struct tevent_req *subreq)
260 {
261         NTSTATUS status = cli_symlink_recv(subreq);
262         tevent_req_simple_finish_ntstatus(subreq, status);
263 }
264
265 NTSTATUS cli_symlink_recv(struct tevent_req *req)
266 {
267         return tevent_req_simple_recv_ntstatus(req);
268 }
269
270 NTSTATUS cli_symlink(struct cli_state *cli, const char *link_target,
271                      const char *newname, uint32_t flags)
272 {
273         TALLOC_CTX *frame = talloc_stackframe();
274         struct tevent_context *ev;
275         struct tevent_req *req;
276         NTSTATUS status = NT_STATUS_NO_MEMORY;
277
278         if (smbXcli_conn_has_async_calls(cli->conn)) {
279                 status = NT_STATUS_INVALID_PARAMETER;
280                 goto fail;
281         }
282         ev = samba_tevent_context_init(frame);
283         if (ev == NULL) {
284                 goto fail;
285         }
286         req = cli_symlink_send(frame, ev, cli, link_target, newname, flags);
287         if (req == NULL) {
288                 goto fail;
289         }
290         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
291                 goto fail;
292         }
293         status = cli_symlink_recv(req);
294  fail:
295         TALLOC_FREE(frame);
296         return status;
297 }
298
299 struct cli_get_reparse_data_state {
300         struct tevent_context *ev;
301         struct cli_state *cli;
302         uint16_t fnum;
303
304         NTSTATUS get_reparse_status;
305         uint8_t *data;
306         uint32_t datalen;
307 };
308
309 static void cli_get_reparse_data_opened(struct tevent_req *subreq);
310 static void cli_get_reparse_data_done(struct tevent_req *subreq);
311 static void cli_get_reparse_data_closed(struct tevent_req *subreq);
312
313 struct tevent_req *cli_get_reparse_data_send(TALLOC_CTX *mem_ctx,
314                                              struct tevent_context *ev,
315                                              struct cli_state *cli,
316                                              const char *fname)
317 {
318         struct tevent_req *req = NULL, *subreq = NULL;
319         struct cli_get_reparse_data_state *state = NULL;
320
321         req = tevent_req_create(mem_ctx,
322                                 &state,
323                                 struct cli_get_reparse_data_state);
324         if (req == NULL) {
325                 return NULL;
326         }
327         state->ev = ev;
328         state->cli = cli;
329
330         subreq = cli_ntcreate_send(state,
331                                    ev,
332                                    cli,
333                                    fname,
334                                    0,
335                                    FILE_READ_ATTRIBUTES | FILE_READ_EA,
336                                    0,
337                                    FILE_SHARE_READ | FILE_SHARE_WRITE |
338                                            FILE_SHARE_DELETE,
339                                    FILE_OPEN,
340                                    FILE_OPEN_REPARSE_POINT,
341                                    SMB2_IMPERSONATION_IMPERSONATION,
342                                    0);
343         if (tevent_req_nomem(subreq, req)) {
344                 return tevent_req_post(req, ev);
345         }
346         tevent_req_set_callback(subreq, cli_get_reparse_data_opened, req);
347         return req;
348 }
349
350 static void cli_get_reparse_data_opened(struct tevent_req *subreq)
351 {
352         struct tevent_req *req =
353                 tevent_req_callback_data(subreq, struct tevent_req);
354         struct cli_get_reparse_data_state *state =
355                 tevent_req_data(req, struct cli_get_reparse_data_state);
356         NTSTATUS status;
357
358         status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
359         TALLOC_FREE(subreq);
360         if (tevent_req_nterror(req, status)) {
361                 return;
362         }
363
364         subreq = cli_fsctl_send(state,
365                                 state->ev,
366                                 state->cli,
367                                 state->fnum,
368                                 FSCTL_GET_REPARSE_POINT,
369                                 NULL,
370                                 65536);
371
372         if (tevent_req_nomem(subreq, req)) {
373                 return;
374         }
375         tevent_req_set_callback(subreq, cli_get_reparse_data_done, req);
376 }
377
378 static void cli_get_reparse_data_done(struct tevent_req *subreq)
379 {
380         struct tevent_req *req =
381                 tevent_req_callback_data(subreq, struct tevent_req);
382         struct cli_get_reparse_data_state *state =
383                 tevent_req_data(req, struct cli_get_reparse_data_state);
384         DATA_BLOB out = {
385                 .data = NULL,
386         };
387
388         state->get_reparse_status = cli_fsctl_recv(subreq, state, &out);
389         TALLOC_FREE(subreq);
390
391         if (NT_STATUS_IS_OK(state->get_reparse_status)) {
392                 state->data = out.data;
393                 state->datalen = out.length;
394         }
395
396         subreq = cli_close_send(state, state->ev, state->cli, state->fnum, 0);
397         if (tevent_req_nomem(subreq, req)) {
398                 return;
399         }
400         tevent_req_set_callback(subreq, cli_get_reparse_data_closed, req);
401 }
402
403 static void cli_get_reparse_data_closed(struct tevent_req *subreq)
404 {
405         struct tevent_req *req =
406                 tevent_req_callback_data(subreq, struct tevent_req);
407         struct cli_get_reparse_data_state *state =
408                 tevent_req_data(req, struct cli_get_reparse_data_state);
409         NTSTATUS status;
410
411         status = cli_close_recv(subreq);
412         TALLOC_FREE(subreq);
413         if (tevent_req_nterror(req, status)) {
414                 return;
415         }
416         if (tevent_req_nterror(req, state->get_reparse_status)) {
417                 return;
418         }
419         tevent_req_done(req);
420 }
421
422 NTSTATUS cli_get_reparse_data_recv(struct tevent_req *req,
423                                    TALLOC_CTX *mem_ctx,
424                                    uint8_t **_data,
425                                    uint32_t *_datalen)
426 {
427         struct cli_get_reparse_data_state *state =
428                 tevent_req_data(req, struct cli_get_reparse_data_state);
429         NTSTATUS status;
430
431         if (tevent_req_is_nterror(req, &status)) {
432                 return status;
433         }
434
435         *_data = talloc_move(mem_ctx, &state->data);
436         *_datalen = state->datalen;
437
438         tevent_req_received(req);
439
440         return NT_STATUS_OK;
441 }
442
443 NTSTATUS cli_get_reparse_data(struct cli_state *cli,
444                               const char *fname,
445                               TALLOC_CTX *mem_ctx,
446                               uint8_t **_data,
447                               uint32_t *_datalen)
448 {
449         TALLOC_CTX *frame = talloc_stackframe();
450         struct tevent_context *ev;
451         struct tevent_req *req;
452         NTSTATUS status = NT_STATUS_NO_MEMORY;
453
454         if (smbXcli_conn_has_async_calls(cli->conn)) {
455                 status = NT_STATUS_INVALID_PARAMETER;
456                 goto fail;
457         }
458         ev = samba_tevent_context_init(frame);
459         if (ev == NULL) {
460                 goto fail;
461         }
462         req = cli_get_reparse_data_send(frame, ev, cli, fname);
463         if (req == NULL) {
464                 goto fail;
465         }
466         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
467                 goto fail;
468         }
469         status = cli_get_reparse_data_recv(req, mem_ctx, _data, _datalen);
470 fail:
471         TALLOC_FREE(frame);
472         return status;
473 }
474
475 struct cli_readlink_state {
476         struct tevent_context *ev;
477         struct cli_state *cli;
478         uint16_t fnum;
479
480         uint16_t setup[4];
481         uint8_t *data;
482         uint32_t num_data;
483         char *target;
484 };
485
486 static void cli_readlink_posix1_done(struct tevent_req *subreq);
487 static void cli_readlink_got_reparse_data(struct tevent_req *subreq);
488
489 struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx,
490                                      struct tevent_context *ev,
491                                      struct cli_state *cli,
492                                      const char *fname)
493 {
494         struct tevent_req *req, *subreq;
495         struct cli_readlink_state *state;
496
497         req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state);
498         if (req == NULL) {
499                 return NULL;
500         }
501         state->ev = ev;
502         state->cli = cli;
503
504         if (cli->requested_posix_capabilities != 0) {
505                 /*
506                  * Only happens for negotiated SMB1 posix caps
507                  */
508                 subreq = cli_posix_readlink_send(state, ev, cli, fname);
509                 if (tevent_req_nomem(subreq, req)) {
510                         return tevent_req_post(req, ev);
511                 }
512                 tevent_req_set_callback(subreq, cli_readlink_posix1_done, req);
513                 return req;
514         }
515
516         subreq = cli_get_reparse_data_send(state, ev, cli, fname);
517         if (tevent_req_nomem(subreq, req)) {
518                 return tevent_req_post(req, ev);
519         }
520         tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req);
521         return req;
522 }
523
524 static void cli_readlink_posix1_done(struct tevent_req *subreq)
525 {
526         struct tevent_req *req = tevent_req_callback_data(
527                 subreq, struct tevent_req);
528         struct cli_readlink_state *state = tevent_req_data(
529                 req, struct cli_readlink_state);
530         NTSTATUS status;
531
532         status = cli_posix_readlink_recv(subreq, state, &state->target);
533         TALLOC_FREE(subreq);
534         if (tevent_req_nterror(req, status)) {
535                 return;
536         }
537         tevent_req_done(req);
538 }
539
540 static void cli_readlink_got_reparse_data(struct tevent_req *subreq)
541 {
542         struct tevent_req *req = tevent_req_callback_data(
543                 subreq, struct tevent_req);
544         struct cli_readlink_state *state = tevent_req_data(
545                 req, struct cli_readlink_state);
546         NTSTATUS status;
547
548         status = cli_get_reparse_data_recv(subreq,
549                                            state,
550                                            &state->data,
551                                            &state->num_data);
552         TALLOC_FREE(subreq);
553         if (tevent_req_nterror(req, status)) {
554                 return;
555         }
556         tevent_req_done(req);
557 }
558
559 NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
560                            char **psubstitute_name, char **pprint_name,
561                            uint32_t *pflags)
562 {
563         struct cli_readlink_state *state = tevent_req_data(
564                 req, struct cli_readlink_state);
565         struct reparse_data_buffer buf = {
566                 .tag = 0,
567         };
568         NTSTATUS status;
569
570         if (tevent_req_is_nterror(req, &status)) {
571                 return status;
572         }
573
574         if (state->target != NULL) {
575                 /*
576                  * SMB1 posix version
577                  */
578                 if (psubstitute_name != NULL) {
579                         *psubstitute_name = talloc_move(
580                                 mem_ctx, &state->target);
581                 }
582                 if (pprint_name != NULL) {
583                         *pprint_name = NULL;
584                 }
585                 if (pflags != NULL) {
586                         *pflags = 0;
587                 }
588                 return NT_STATUS_OK;
589         }
590
591         status = reparse_data_buffer_parse(state,
592                                            &buf,
593                                            state->data,
594                                            state->num_data);
595         if (!NT_STATUS_IS_OK(status)) {
596                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
597         }
598         if (buf.tag != IO_REPARSE_TAG_SYMLINK) {
599                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
600         }
601
602         if (psubstitute_name != NULL) {
603                 *psubstitute_name =
604                         talloc_move(mem_ctx, &buf.parsed.lnk.substitute_name);
605         }
606
607         if (pprint_name != NULL) {
608                 *pprint_name =
609                         talloc_move(mem_ctx, &buf.parsed.lnk.print_name);
610         }
611
612         if (pflags != NULL) {
613                 *pflags = buf.parsed.lnk.flags;
614         }
615
616         tevent_req_received(req);
617
618         return NT_STATUS_OK;
619 }
620
621 NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
622                        TALLOC_CTX *mem_ctx, char **psubstitute_name,
623                       char **pprint_name, uint32_t *pflags)
624 {
625         TALLOC_CTX *frame = talloc_stackframe();
626         struct tevent_context *ev;
627         struct tevent_req *req;
628         NTSTATUS status = NT_STATUS_NO_MEMORY;
629
630         if (smbXcli_conn_has_async_calls(cli->conn)) {
631                 status = NT_STATUS_INVALID_PARAMETER;
632                 goto fail;
633         }
634         ev = samba_tevent_context_init(frame);
635         if (ev == NULL) {
636                 goto fail;
637         }
638         req = cli_readlink_send(frame, ev, cli, fname);
639         if (req == NULL) {
640                 goto fail;
641         }
642         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
643                 goto fail;
644         }
645         status = cli_readlink_recv(req, mem_ctx, psubstitute_name,
646                                    pprint_name, pflags);
647  fail:
648         TALLOC_FREE(frame);
649         return status;
650 }