1 /*
2    Unix SMB/CIFS implementation.
3    smb2 lib
4    Copyright (C) Volker Lendecke 2017
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/network.h"
22 #include "lib/util/tevent_ntstatus.h"
23 #include "smb_common.h"
24 #include "smbXcli_base.h"
25 #include "librpc/gen_ndr/ndr_notify.h"
26 
27 struct smb2cli_notify_state {
28 	uint8_t fixed[32];
29 
30 	struct iovec *recv_iov;
31 	uint8_t *data;
32 	uint32_t data_length;
33 
34 	struct tevent_req *subreq;
35 	struct tevent_req *timeout_subreq;
36 };
37 
38 static void smb2cli_notify_done(struct tevent_req *subreq);
39 static void smb2cli_notify_timedout(struct tevent_req *subreq);
40 static bool smb2cli_notify_cancel(struct tevent_req *req);
41 
smb2cli_notify_send(TALLOC_CTX * mem_ctx,struct tevent_context * ev,struct smbXcli_conn * conn,uint32_t timeout_msec,struct smbXcli_session * session,struct smbXcli_tcon * tcon,uint32_t output_buffer_length,uint64_t fid_persistent,uint64_t fid_volatile,uint32_t completion_filter,bool recursive)42 struct tevent_req *smb2cli_notify_send(TALLOC_CTX *mem_ctx,
43 				       struct tevent_context *ev,
44 				       struct smbXcli_conn *conn,
45 				       uint32_t timeout_msec,
46 				       struct smbXcli_session *session,
47 				       struct smbXcli_tcon *tcon,
48 				       uint32_t output_buffer_length,
49 				       uint64_t fid_persistent,
50 				       uint64_t fid_volatile,
51 				       uint32_t completion_filter,
52 				       bool recursive)
53 {
54 	struct tevent_req *req;
55 	struct smb2cli_notify_state *state;
56 	uint8_t *fixed;
57 	uint16_t watch_tree;
58 
59 	req = tevent_req_create(mem_ctx, &state,
60 				struct smb2cli_notify_state);
61 	if (req == NULL) {
62 		return NULL;
63 	}
64 
65 	watch_tree = recursive ? SMB2_WATCH_TREE : 0;
66 	fixed = state->fixed;
67 	SSVAL(fixed, 0, 32);
68 	SSVAL(fixed, 2, watch_tree);
69 	SIVAL(fixed, 4, output_buffer_length);
70 	SBVAL(fixed, 8, fid_persistent);
71 	SBVAL(fixed, 16, fid_volatile);
72 	SIVAL(fixed, 24, completion_filter);
73 	SIVAL(fixed, 28, 0); 	/* reserved */
74 
75 	state->subreq = smb2cli_req_send(state, ev, conn, SMB2_OP_NOTIFY,
76 					 0, 0, /* flags */
77 					 0,	/* timeout_msec */
78 					 tcon,
79 					 session,
80 					 state->fixed, sizeof(state->fixed),
81 					 NULL, 0, /* dyn* */
82 					 0); /* max_dyn_len */
83 	if (tevent_req_nomem(state->subreq, req)) {
84 		return tevent_req_post(req, ev);
85 	}
86 	tevent_req_set_callback(state->subreq, smb2cli_notify_done, req);
87 
88 	if (timeout_msec != 0) {
89 		state->timeout_subreq = tevent_wakeup_send(
90 			state, ev, timeval_current_ofs_msec(timeout_msec));
91 		if (tevent_req_nomem(state->timeout_subreq, req)) {
92 			return tevent_req_post(req, ev);
93 		}
94 		tevent_req_set_callback(
95 			state->timeout_subreq, smb2cli_notify_timedout, req);
96 	}
97 
98 	tevent_req_set_cancel_fn(req, smb2cli_notify_cancel);
99 
100 	return req;
101 }
102 
smb2cli_notify_cancel(struct tevent_req * req)103 static bool smb2cli_notify_cancel(struct tevent_req *req)
104 {
105 	struct smb2cli_notify_state *state = tevent_req_data(
106 		req, struct smb2cli_notify_state);
107 	bool ok;
108 
109 	TALLOC_FREE(state->timeout_subreq);
110 
111 	ok = tevent_req_cancel(state->subreq);
112 	return ok;
113 }
114 
smb2cli_notify_timedout(struct tevent_req * subreq)115 static void smb2cli_notify_timedout(struct tevent_req *subreq)
116 {
117 	struct tevent_req *req = tevent_req_callback_data(
118 		subreq, struct tevent_req);
119 	struct smb2cli_notify_state *state = tevent_req_data(
120 		req, struct smb2cli_notify_state);
121 	bool ok;
122 
123 	ok = tevent_wakeup_recv(subreq);
124 	if (!ok) {
125 		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
126 		return;
127 	}
128 
129 	ok = tevent_req_cancel(state->subreq);
130 	if (!ok) {
131 		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
132 		return;
133 	}
134 }
135 
smb2cli_notify_done(struct tevent_req * subreq)136 static void smb2cli_notify_done(struct tevent_req *subreq)
137 {
138 	struct tevent_req *req = tevent_req_callback_data(
139 		subreq, struct tevent_req);
140 	struct smb2cli_notify_state *state = tevent_req_data(
141 		req, struct smb2cli_notify_state);
142 	NTSTATUS status;
143 	struct iovec *iov;
144 	uint16_t data_offset;
145 	static const struct smb2cli_req_expected_response expected[] = {
146 	{
147 		.status = NT_STATUS_OK,
148 		.body_size = 0x09
149 	}
150 	};
151 
152 	status = smb2cli_req_recv(subreq, state, &iov,
153 				  expected, ARRAY_SIZE(expected));
154 	TALLOC_FREE(subreq);
155 
156 	if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) {
157 		status = NT_STATUS_IO_TIMEOUT;
158 	}
159 	if (tevent_req_nterror(req, status)) {
160 		return;
161 	}
162 
163 	data_offset = SVAL(iov[1].iov_base, 2);
164 	state->data_length = IVAL(iov[1].iov_base, 4);
165 
166 	if ((data_offset != SMB2_HDR_BODY + 8) ||
167 	    (state->data_length > iov[2].iov_len)) {
168 		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
169 		return;
170 	}
171 
172 	state->recv_iov = iov;
173 	state->data = (uint8_t *)iov[2].iov_base;
174 	tevent_req_done(req);
175 }
176 
smb2cli_notify_recv(struct tevent_req * req,TALLOC_CTX * mem_ctx,uint8_t ** data,uint32_t * data_length)177 NTSTATUS smb2cli_notify_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
178 			     uint8_t **data, uint32_t *data_length)
179 {
180 	struct smb2cli_notify_state *state = tevent_req_data(
181 		req, struct smb2cli_notify_state);
182 	NTSTATUS status;
183 
184 	if (tevent_req_is_nterror(req, &status)) {
185 		return status;
186 	}
187 	talloc_steal(mem_ctx, state->recv_iov);
188 	*data_length = state->data_length;
189 	*data = state->data;
190 	return NT_STATUS_OK;
191 }
192 
smb2cli_notify(struct smbXcli_conn * conn,uint32_t timeout_msec,struct smbXcli_session * session,struct smbXcli_tcon * tcon,uint32_t output_buffer_length,uint64_t fid_persistent,uint64_t fid_volatile,uint32_t completion_filter,bool recursive,TALLOC_CTX * mem_ctx,uint8_t ** data,uint32_t * data_length)193 NTSTATUS smb2cli_notify(struct smbXcli_conn *conn,
194 			uint32_t timeout_msec,
195 			struct smbXcli_session *session,
196 			struct smbXcli_tcon *tcon,
197 			uint32_t output_buffer_length,
198 			uint64_t fid_persistent,
199 			uint64_t fid_volatile,
200 			uint32_t completion_filter,
201 			bool recursive,
202 			TALLOC_CTX *mem_ctx,
203 			uint8_t **data,
204 			uint32_t *data_length)
205 {
206 	TALLOC_CTX *frame = talloc_stackframe();
207 	struct tevent_context *ev;
208 	struct tevent_req *req;
209 	NTSTATUS status = NT_STATUS_NO_MEMORY;
210 
211 	if (smbXcli_conn_has_async_calls(conn)) {
212 		/*
213 		 * Can't use sync call while an async call is in flight
214 		 */
215 		status = NT_STATUS_INVALID_PARAMETER;
216 		goto fail;
217 	}
218 	ev = samba_tevent_context_init(frame);
219 	if (ev == NULL) {
220 		goto fail;
221 	}
222 	req = smb2cli_notify_send(frame, ev, conn, timeout_msec,
223 				  session, tcon, output_buffer_length,
224 				  fid_persistent, fid_volatile,
225 				  completion_filter, recursive);
226 	if (req == NULL) {
227 		goto fail;
228 	}
229 	if (!tevent_req_poll_ntstatus(req, ev, &status)) {
230 		goto fail;
231 	}
232 	status = smb2cli_notify_recv(req, mem_ctx, data, data_length);
233  fail:
234 	TALLOC_FREE(frame);
235 	return status;
236 }
237