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