Line data Source code
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 4 : 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 4 : req = tevent_req_create(mem_ctx, &state, struct cli_symlink_state);
61 4 : if (req == NULL) {
62 0 : return NULL;
63 : }
64 4 : state->ev = ev;
65 4 : state->cli = cli;
66 4 : state->link_target = link_target;
67 4 : state->newpath = newpath;
68 4 : state->flags = flags;
69 :
70 4 : subreq = cli_ntcreate_send(
71 4 : 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 4 : if (tevent_req_nomem(subreq, req)) {
79 0 : return tevent_req_post(req, ev);
80 : }
81 4 : tevent_req_set_callback(subreq, cli_symlink_create_done, req);
82 4 : return req;
83 : }
84 :
85 4 : static void cli_symlink_create_done(struct tevent_req *subreq)
86 : {
87 4 : struct tevent_req *req = tevent_req_callback_data(
88 : subreq, struct tevent_req);
89 4 : struct cli_symlink_state *state = tevent_req_data(
90 : req, struct cli_symlink_state);
91 : NTSTATUS status;
92 :
93 4 : status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
94 4 : TALLOC_FREE(subreq);
95 4 : if (tevent_req_nterror(req, status)) {
96 0 : return;
97 : }
98 :
99 4 : if (!symlink_reparse_buffer_marshall(
100 : state->link_target, NULL, 0, state->flags, state,
101 : &state->in.data, &state->in.length)) {
102 0 : tevent_req_oom(req);
103 0 : return;
104 : }
105 :
106 4 : subreq = cli_fsctl_send(
107 : state,
108 : state->ev,
109 : state->cli,
110 4 : state->fnum,
111 : FSCTL_SET_REPARSE_POINT,
112 4 : &state->in,
113 : 0);
114 4 : if (tevent_req_nomem(subreq, req)) {
115 0 : return;
116 : }
117 4 : tevent_req_set_callback(subreq, cli_symlink_set_reparse_done, req);
118 : }
119 :
120 4 : static void cli_symlink_set_reparse_done(struct tevent_req *subreq)
121 : {
122 4 : struct tevent_req *req = tevent_req_callback_data(
123 : subreq, struct tevent_req);
124 4 : struct cli_symlink_state *state = tevent_req_data(
125 : req, struct cli_symlink_state);
126 :
127 4 : state->set_reparse_status = cli_fsctl_recv(subreq, NULL, NULL);
128 4 : TALLOC_FREE(subreq);
129 :
130 4 : if (NT_STATUS_IS_OK(state->set_reparse_status)) {
131 0 : subreq = cli_close_send(state, state->ev, state->cli,
132 0 : state->fnum);
133 0 : if (tevent_req_nomem(subreq, req)) {
134 0 : return;
135 : }
136 0 : tevent_req_set_callback(subreq, cli_symlink_close_done, req);
137 0 : return;
138 : }
139 4 : subreq = cli_nt_delete_on_close_send(
140 4 : state, state->ev, state->cli, state->fnum, true);
141 4 : if (tevent_req_nomem(subreq, req)) {
142 0 : return;
143 : }
144 4 : tevent_req_set_callback(subreq, cli_symlink_delete_on_close_done, req);
145 : }
146 :
147 4 : static void cli_symlink_delete_on_close_done(struct tevent_req *subreq)
148 : {
149 4 : struct tevent_req *req = tevent_req_callback_data(
150 : subreq, struct tevent_req);
151 4 : 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 4 : (void)cli_nt_delete_on_close_recv(subreq);
159 4 : TALLOC_FREE(subreq);
160 :
161 4 : subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
162 4 : if (tevent_req_nomem(subreq, req)) {
163 0 : return;
164 : }
165 4 : tevent_req_set_callback(subreq, cli_symlink_close_done, req);
166 : }
167 :
168 4 : static void cli_symlink_close_done(struct tevent_req *subreq)
169 : {
170 4 : struct tevent_req *req = tevent_req_callback_data(
171 : subreq, struct tevent_req);
172 4 : struct cli_symlink_state *state = tevent_req_data(
173 : req, struct cli_symlink_state);
174 : NTSTATUS status;
175 :
176 4 : status = cli_close_recv(subreq);
177 4 : TALLOC_FREE(subreq);
178 :
179 4 : if (tevent_req_nterror(req, status)) {
180 4 : return;
181 : }
182 4 : if (tevent_req_nterror(req, state->set_reparse_status)) {
183 4 : return;
184 : }
185 0 : tevent_req_done(req);
186 : }
187 :
188 4 : NTSTATUS cli_symlink_recv(struct tevent_req *req)
189 : {
190 4 : return tevent_req_simple_recv_ntstatus(req);
191 : }
192 :
193 4 : NTSTATUS cli_symlink(struct cli_state *cli, const char *link_target,
194 : const char *newname, uint32_t flags)
195 : {
196 4 : TALLOC_CTX *frame = talloc_stackframe();
197 : struct tevent_context *ev;
198 : struct tevent_req *req;
199 4 : NTSTATUS status = NT_STATUS_NO_MEMORY;
200 :
201 4 : if (smbXcli_conn_has_async_calls(cli->conn)) {
202 0 : status = NT_STATUS_INVALID_PARAMETER;
203 0 : goto fail;
204 : }
205 4 : ev = samba_tevent_context_init(frame);
206 4 : if (ev == NULL) {
207 0 : goto fail;
208 : }
209 4 : req = cli_symlink_send(frame, ev, cli, link_target, newname, flags);
210 4 : if (req == NULL) {
211 0 : goto fail;
212 : }
213 4 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
214 0 : goto fail;
215 : }
216 4 : status = cli_symlink_recv(req);
217 4 : fail:
218 4 : TALLOC_FREE(frame);
219 4 : 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 0 : 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 0 : req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state);
248 0 : if (req == NULL) {
249 0 : return NULL;
250 : }
251 0 : state->ev = ev;
252 0 : state->cli = cli;
253 :
254 0 : if (cli->requested_posix_capabilities != 0) {
255 : /*
256 : * Only happens for negotiated SMB1 posix caps
257 : */
258 0 : subreq = cli_posix_readlink_send(state, ev, cli, fname);
259 0 : if (tevent_req_nomem(subreq, req)) {
260 0 : return tevent_req_post(req, ev);
261 : }
262 0 : tevent_req_set_callback(subreq, cli_readlink_posix1_done, req);
263 0 : return req;
264 : }
265 :
266 0 : 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 0 : if (tevent_req_nomem(subreq, req)) {
272 0 : return tevent_req_post(req, ev);
273 : }
274 0 : tevent_req_set_callback(subreq, cli_readlink_opened, req);
275 0 : return req;
276 : }
277 :
278 0 : static void cli_readlink_posix1_done(struct tevent_req *subreq)
279 : {
280 0 : struct tevent_req *req = tevent_req_callback_data(
281 : subreq, struct tevent_req);
282 0 : struct cli_readlink_state *state = tevent_req_data(
283 : req, struct cli_readlink_state);
284 : NTSTATUS status;
285 :
286 0 : status = cli_posix_readlink_recv(subreq, state, &state->target);
287 0 : TALLOC_FREE(subreq);
288 0 : if (tevent_req_nterror(req, status)) {
289 0 : return;
290 : }
291 0 : tevent_req_done(req);
292 : }
293 :
294 0 : static void cli_readlink_opened(struct tevent_req *subreq)
295 : {
296 0 : struct tevent_req *req = tevent_req_callback_data(
297 : subreq, struct tevent_req);
298 0 : struct cli_readlink_state *state = tevent_req_data(
299 : req, struct cli_readlink_state);
300 : NTSTATUS status;
301 :
302 0 : status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
303 0 : TALLOC_FREE(subreq);
304 0 : if (tevent_req_nterror(req, status)) {
305 0 : return;
306 : }
307 :
308 0 : subreq = cli_fsctl_send(
309 : state,
310 : state->ev,
311 : state->cli,
312 0 : state->fnum,
313 : FSCTL_GET_REPARSE_POINT,
314 : NULL,
315 : 65536);
316 :
317 0 : if (tevent_req_nomem(subreq, req)) {
318 0 : return;
319 : }
320 0 : tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req);
321 : }
322 :
323 0 : static void cli_readlink_got_reparse_data(struct tevent_req *subreq)
324 : {
325 0 : struct tevent_req *req = tevent_req_callback_data(
326 : subreq, struct tevent_req);
327 0 : struct cli_readlink_state *state = tevent_req_data(
328 : req, struct cli_readlink_state);
329 0 : DATA_BLOB out = { .data = NULL, };
330 :
331 0 : state->get_reparse_status = cli_fsctl_recv(subreq, state, &out);
332 0 : TALLOC_FREE(subreq);
333 :
334 0 : if (NT_STATUS_IS_OK(state->get_reparse_status)) {
335 0 : state->data = out.data;
336 0 : state->num_data = out.length;
337 : }
338 :
339 0 : subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
340 0 : if (tevent_req_nomem(subreq, req)) {
341 0 : return;
342 : }
343 0 : tevent_req_set_callback(subreq, cli_readlink_closed, req);
344 : }
345 :
346 0 : static void cli_readlink_closed(struct tevent_req *subreq)
347 : {
348 0 : struct tevent_req *req = tevent_req_callback_data(
349 : subreq, struct tevent_req);
350 0 : struct cli_readlink_state *state = tevent_req_data(
351 : req, struct cli_readlink_state);
352 : NTSTATUS status;
353 :
354 0 : status = cli_close_recv(subreq);
355 0 : TALLOC_FREE(subreq);
356 0 : if (tevent_req_nterror(req, status)) {
357 0 : return;
358 : }
359 0 : if (tevent_req_nterror(req, state->get_reparse_status)) {
360 0 : return;
361 : }
362 0 : tevent_req_done(req);
363 : }
364 :
365 0 : 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 0 : struct cli_readlink_state *state = tevent_req_data(
370 : req, struct cli_readlink_state);
371 0 : struct symlink_reparse_struct *symlink = NULL;
372 : NTSTATUS status;
373 :
374 0 : if (tevent_req_is_nterror(req, &status)) {
375 0 : return status;
376 : }
377 :
378 0 : if (state->target != NULL) {
379 : /*
380 : * SMB1 posix version
381 : */
382 0 : if (psubstitute_name != NULL) {
383 0 : *psubstitute_name = talloc_move(
384 : mem_ctx, &state->target);
385 : }
386 0 : if (pprint_name != NULL) {
387 0 : *pprint_name = NULL;
388 : }
389 0 : if (pflags != NULL) {
390 0 : *pflags = 0;
391 : }
392 0 : return NT_STATUS_OK;
393 : }
394 :
395 0 : symlink = symlink_reparse_buffer_parse(
396 0 : talloc_tos(), state->data, state->num_data);
397 0 : if (symlink == NULL) {
398 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
399 : }
400 :
401 0 : if (psubstitute_name != NULL) {
402 0 : *psubstitute_name = talloc_move(
403 : mem_ctx, &symlink->substitute_name);
404 : }
405 :
406 0 : if (pprint_name != NULL) {
407 0 : *pprint_name = talloc_move(mem_ctx, &symlink->print_name);
408 : }
409 :
410 0 : if (pflags != NULL) {
411 0 : *pflags = symlink->flags;
412 : }
413 :
414 0 : TALLOC_FREE(symlink);
415 :
416 0 : return NT_STATUS_OK;
417 : }
418 :
419 0 : NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
420 : TALLOC_CTX *mem_ctx, char **psubstitute_name,
421 : char **pprint_name, uint32_t *pflags)
422 : {
423 0 : TALLOC_CTX *frame = talloc_stackframe();
424 : struct tevent_context *ev;
425 : struct tevent_req *req;
426 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
427 :
428 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
429 0 : status = NT_STATUS_INVALID_PARAMETER;
430 0 : goto fail;
431 : }
432 0 : ev = samba_tevent_context_init(frame);
433 0 : if (ev == NULL) {
434 0 : goto fail;
435 : }
436 0 : req = cli_readlink_send(frame, ev, cli, fname);
437 0 : if (req == NULL) {
438 0 : goto fail;
439 : }
440 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
441 0 : goto fail;
442 : }
443 0 : status = cli_readlink_recv(req, mem_ctx, psubstitute_name,
444 : pprint_name, pflags);
445 0 : fail:
446 0 : TALLOC_FREE(frame);
447 0 : return status;
448 : }
|