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 :
41 : uint16_t setup[4];
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 : DATA_BLOB data;
92 : NTSTATUS status;
93 :
94 4 : status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
95 4 : TALLOC_FREE(subreq);
96 4 : if (tevent_req_nterror(req, status)) {
97 0 : return;
98 : }
99 :
100 4 : if (!symlink_reparse_buffer_marshall(
101 : state->link_target, NULL, state->flags, state,
102 : &data.data, &data.length)) {
103 0 : tevent_req_oom(req);
104 0 : return;
105 : }
106 :
107 4 : if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
108 4 : subreq = cli_smb2_set_reparse_point_fnum_send(state,
109 : state->ev,
110 : state->cli,
111 4 : state->fnum,
112 : data);
113 : } else {
114 0 : SIVAL(state->setup, 0, FSCTL_SET_REPARSE_POINT);
115 0 : SSVAL(state->setup, 4, state->fnum);
116 0 : SCVAL(state->setup, 6, 1); /* IsFcntl */
117 0 : SCVAL(state->setup, 7, 0); /* IsFlags */
118 :
119 :
120 0 : subreq = cli_trans_send(state, state->ev, state->cli, 0,
121 : SMBnttrans,
122 : NULL, -1, /* name, fid */
123 : NT_TRANSACT_IOCTL, 0,
124 0 : state->setup, 4, 0, /* setup */
125 : NULL, 0, 0, /* param */
126 0 : data.data, data.length, 0); /* data */
127 : }
128 :
129 4 : if (tevent_req_nomem(subreq, req)) {
130 0 : return;
131 : }
132 4 : tevent_req_set_callback(subreq, cli_symlink_set_reparse_done, req);
133 : }
134 :
135 4 : static void cli_symlink_set_reparse_done(struct tevent_req *subreq)
136 : {
137 4 : struct tevent_req *req = tevent_req_callback_data(
138 : subreq, struct tevent_req);
139 4 : struct cli_symlink_state *state = tevent_req_data(
140 : req, struct cli_symlink_state);
141 :
142 4 : if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
143 2 : state->set_reparse_status =
144 2 : cli_smb2_set_reparse_point_fnum_recv(subreq);
145 : } else {
146 0 : state->set_reparse_status = cli_trans_recv(
147 : subreq, NULL, NULL,
148 : NULL, 0, NULL, /* rsetup */
149 : NULL, 0, NULL, /* rparam */
150 : NULL, 0, NULL); /* rdata */
151 : }
152 4 : TALLOC_FREE(subreq);
153 :
154 4 : if (NT_STATUS_IS_OK(state->set_reparse_status)) {
155 0 : subreq = cli_close_send(state, state->ev, state->cli,
156 0 : state->fnum);
157 0 : if (tevent_req_nomem(subreq, req)) {
158 0 : return;
159 : }
160 0 : tevent_req_set_callback(subreq, cli_symlink_close_done, req);
161 0 : return;
162 : }
163 4 : subreq = cli_nt_delete_on_close_send(
164 4 : state, state->ev, state->cli, state->fnum, true);
165 4 : if (tevent_req_nomem(subreq, req)) {
166 0 : return;
167 : }
168 4 : tevent_req_set_callback(subreq, cli_symlink_delete_on_close_done, req);
169 : }
170 :
171 4 : static void cli_symlink_delete_on_close_done(struct tevent_req *subreq)
172 : {
173 4 : struct tevent_req *req = tevent_req_callback_data(
174 : subreq, struct tevent_req);
175 4 : struct cli_symlink_state *state = tevent_req_data(
176 : req, struct cli_symlink_state);
177 :
178 : /*
179 : * Ignore status, we can't do much anyway in case of failure
180 : */
181 :
182 4 : (void)cli_nt_delete_on_close_recv(subreq);
183 4 : TALLOC_FREE(subreq);
184 :
185 4 : subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
186 4 : if (tevent_req_nomem(subreq, req)) {
187 0 : return;
188 : }
189 4 : tevent_req_set_callback(subreq, cli_symlink_close_done, req);
190 : }
191 :
192 4 : static void cli_symlink_close_done(struct tevent_req *subreq)
193 : {
194 4 : struct tevent_req *req = tevent_req_callback_data(
195 : subreq, struct tevent_req);
196 4 : struct cli_symlink_state *state = tevent_req_data(
197 : req, struct cli_symlink_state);
198 : NTSTATUS status;
199 :
200 4 : status = cli_close_recv(subreq);
201 4 : TALLOC_FREE(subreq);
202 :
203 4 : if (tevent_req_nterror(req, status)) {
204 4 : return;
205 : }
206 4 : if (tevent_req_nterror(req, state->set_reparse_status)) {
207 4 : return;
208 : }
209 0 : tevent_req_done(req);
210 : }
211 :
212 4 : NTSTATUS cli_symlink_recv(struct tevent_req *req)
213 : {
214 4 : return tevent_req_simple_recv_ntstatus(req);
215 : }
216 :
217 4 : NTSTATUS cli_symlink(struct cli_state *cli, const char *link_target,
218 : const char *newname, uint32_t flags)
219 : {
220 4 : TALLOC_CTX *frame = talloc_stackframe();
221 : struct tevent_context *ev;
222 : struct tevent_req *req;
223 4 : NTSTATUS status = NT_STATUS_NO_MEMORY;
224 :
225 4 : if (smbXcli_conn_has_async_calls(cli->conn)) {
226 0 : status = NT_STATUS_INVALID_PARAMETER;
227 0 : goto fail;
228 : }
229 4 : ev = samba_tevent_context_init(frame);
230 4 : if (ev == NULL) {
231 0 : goto fail;
232 : }
233 4 : req = cli_symlink_send(frame, ev, cli, link_target, newname, flags);
234 4 : if (req == NULL) {
235 0 : goto fail;
236 : }
237 4 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
238 0 : goto fail;
239 : }
240 4 : status = cli_symlink_recv(req);
241 4 : fail:
242 4 : TALLOC_FREE(frame);
243 4 : return status;
244 : }
245 :
246 : struct cli_readlink_state {
247 : struct tevent_context *ev;
248 : struct cli_state *cli;
249 : uint16_t fnum;
250 :
251 : uint16_t setup[4];
252 : NTSTATUS get_reparse_status;
253 : uint8_t *data;
254 : uint32_t num_data;
255 : };
256 :
257 : static void cli_readlink_opened(struct tevent_req *subreq);
258 : static void cli_readlink_got_reparse_data(struct tevent_req *subreq);
259 : static void cli_readlink_closed(struct tevent_req *subreq);
260 :
261 0 : struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx,
262 : struct tevent_context *ev,
263 : struct cli_state *cli,
264 : const char *fname)
265 : {
266 : struct tevent_req *req, *subreq;
267 : struct cli_readlink_state *state;
268 :
269 0 : req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state);
270 0 : if (req == NULL) {
271 0 : return NULL;
272 : }
273 0 : state->ev = ev;
274 0 : state->cli = cli;
275 :
276 0 : subreq = cli_ntcreate_send(
277 : state, ev, cli, fname, 0, FILE_READ_ATTRIBUTES | FILE_READ_EA,
278 : 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
279 : FILE_OPEN, FILE_OPEN_REPARSE_POINT,
280 : SMB2_IMPERSONATION_IMPERSONATION, 0);
281 0 : if (tevent_req_nomem(subreq, req)) {
282 0 : return tevent_req_post(req, ev);
283 : }
284 0 : tevent_req_set_callback(subreq, cli_readlink_opened, req);
285 0 : return req;
286 : }
287 :
288 0 : static void cli_readlink_opened(struct tevent_req *subreq)
289 : {
290 0 : struct tevent_req *req = tevent_req_callback_data(
291 : subreq, struct tevent_req);
292 0 : struct cli_readlink_state *state = tevent_req_data(
293 : req, struct cli_readlink_state);
294 : NTSTATUS status;
295 :
296 0 : status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
297 0 : TALLOC_FREE(subreq);
298 0 : if (tevent_req_nterror(req, status)) {
299 0 : return;
300 : }
301 :
302 0 : if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
303 0 : subreq = cli_smb2_get_reparse_point_fnum_send(state,
304 : state->ev,
305 : state->cli,
306 0 : state->fnum);
307 : } else {
308 0 : SIVAL(state->setup, 0, FSCTL_GET_REPARSE_POINT);
309 0 : SSVAL(state->setup, 4, state->fnum);
310 0 : SCVAL(state->setup, 6, 1); /* IsFcntl */
311 0 : SCVAL(state->setup, 7, 0); /* IsFlags */
312 :
313 0 : subreq = cli_trans_send(state, state->ev, state->cli,
314 : 0, SMBnttrans,
315 : NULL, -1, /* name, fid */
316 : NT_TRANSACT_IOCTL, 0,
317 0 : state->setup, 4, 0, /* setup */
318 : NULL, 0, 0, /* param */
319 : NULL, 0, 16384); /* data */
320 : }
321 :
322 0 : if (tevent_req_nomem(subreq, req)) {
323 0 : return;
324 : }
325 0 : tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req);
326 : }
327 :
328 0 : static void cli_readlink_got_reparse_data(struct tevent_req *subreq)
329 : {
330 0 : struct tevent_req *req = tevent_req_callback_data(
331 : subreq, struct tevent_req);
332 0 : struct cli_readlink_state *state = tevent_req_data(
333 : req, struct cli_readlink_state);
334 :
335 0 : if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
336 : DATA_BLOB recv_data;
337 0 : state->get_reparse_status =
338 0 : cli_smb2_get_reparse_point_fnum_recv(subreq,
339 : state,
340 : &recv_data);
341 0 : if (NT_STATUS_IS_OK(state->get_reparse_status)) {
342 0 : state->data = recv_data.data;
343 0 : state->num_data = recv_data.length;
344 : }
345 : } else {
346 0 : state->get_reparse_status = cli_trans_recv(
347 : subreq, state, NULL,
348 : NULL, 0, NULL, /* rsetup */
349 : NULL, 0, NULL, /* rparam */
350 : &state->data, 20, &state->num_data); /* rdata */
351 : }
352 0 : TALLOC_FREE(subreq);
353 :
354 0 : subreq = cli_close_send(state, state->ev, state->cli, state->fnum);
355 0 : if (tevent_req_nomem(subreq, req)) {
356 0 : return;
357 : }
358 0 : tevent_req_set_callback(subreq, cli_readlink_closed, req);
359 : }
360 :
361 0 : static void cli_readlink_closed(struct tevent_req *subreq)
362 : {
363 0 : struct tevent_req *req = tevent_req_callback_data(
364 : subreq, struct tevent_req);
365 : NTSTATUS status;
366 :
367 0 : status = cli_close_recv(subreq);
368 0 : TALLOC_FREE(subreq);
369 0 : if (tevent_req_nterror(req, status)) {
370 0 : return;
371 : }
372 0 : tevent_req_done(req);
373 : }
374 :
375 0 : NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
376 : char **psubstitute_name, char **pprint_name,
377 : uint32_t *pflags)
378 : {
379 0 : struct cli_readlink_state *state = tevent_req_data(
380 : req, struct cli_readlink_state);
381 0 : struct symlink_reparse_struct *symlink = NULL;
382 : NTSTATUS status;
383 :
384 0 : if (tevent_req_is_nterror(req, &status)) {
385 0 : return status;
386 : }
387 :
388 0 : symlink = symlink_reparse_buffer_parse(
389 0 : talloc_tos(), state->data, state->num_data);
390 0 : if (symlink == NULL) {
391 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
392 : }
393 :
394 0 : if (psubstitute_name != NULL) {
395 0 : *psubstitute_name = talloc_move(
396 : mem_ctx, &symlink->substitute_name);
397 : }
398 :
399 0 : if (pprint_name != NULL) {
400 0 : *pprint_name = talloc_move(mem_ctx, &symlink->print_name);
401 : }
402 :
403 0 : if (pflags != NULL) {
404 0 : *pflags = symlink->flags;
405 : }
406 :
407 0 : TALLOC_FREE(symlink);
408 :
409 0 : return NT_STATUS_OK;
410 : }
411 :
412 0 : NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
413 : TALLOC_CTX *mem_ctx, char **psubstitute_name,
414 : char **pprint_name, uint32_t *pflags)
415 : {
416 0 : TALLOC_CTX *frame = talloc_stackframe();
417 : struct tevent_context *ev;
418 : struct tevent_req *req;
419 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
420 :
421 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
422 0 : status = NT_STATUS_INVALID_PARAMETER;
423 0 : goto fail;
424 : }
425 0 : ev = samba_tevent_context_init(frame);
426 0 : if (ev == NULL) {
427 0 : goto fail;
428 : }
429 0 : req = cli_readlink_send(frame, ev, cli, fname);
430 0 : if (req == NULL) {
431 0 : goto fail;
432 : }
433 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
434 0 : goto fail;
435 : }
436 0 : status = cli_readlink_recv(req, mem_ctx, psubstitute_name,
437 : pprint_name, pflags);
438 0 : fail:
439 0 : TALLOC_FREE(frame);
440 0 : return status;
441 : }
|