Line data Source code
1 : /*
2 : * Unix SMB/CIFS implementation.
3 : *
4 : * Implementation of
5 : * http://msdn.microsoft.com/en-us/library/cc232006%28v=PROT.13%29.aspx
6 : *
7 : * Copyright (C) Volker Lendecke 2011
8 : *
9 : * This program is free software; you can redistribute it and/or modify
10 : * it under the terms of the GNU General Public License as published by
11 : * the Free Software Foundation; either version 3 of the License, or
12 : * (at your option) any later version.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "replace.h"
24 : #include "reparse_symlink.h"
25 : #include "lib/util/charset/charset.h"
26 : #include "lib/util/byteorder.h"
27 : #include "libcli/smb/smb_constants.h"
28 : #include "libcli/smb/smb_util.h"
29 : #include "lib/util/debug.h"
30 :
31 8 : ssize_t reparse_buffer_marshall(
32 : uint32_t reparse_tag,
33 : uint16_t reserved,
34 : const struct iovec *iov,
35 : int iovlen,
36 : uint8_t *buf,
37 : size_t buflen)
38 : {
39 8 : ssize_t reparse_data_length = iov_buflen(iov, iovlen);
40 : size_t needed;
41 :
42 8 : if (reparse_data_length == -1) {
43 0 : return -1;
44 : }
45 8 : if (reparse_data_length > UINT16_MAX) {
46 0 : return -1;
47 : }
48 :
49 8 : needed = reparse_data_length + 8;
50 8 : if (needed < reparse_data_length) {
51 0 : return -1;
52 : }
53 :
54 8 : if (buflen >= needed) {
55 4 : PUSH_LE_U32(buf, 0, reparse_tag);
56 4 : PUSH_LE_U16(buf, 4, reparse_data_length);
57 4 : PUSH_LE_U16(buf, 6, reserved);
58 4 : iov_buf(iov, iovlen, buf+8, buflen-8);
59 : }
60 :
61 8 : return needed;
62 : }
63 :
64 4 : bool symlink_reparse_buffer_marshall(
65 : const char *substitute,
66 : const char *printname,
67 : uint16_t unparsed_path_length,
68 : uint32_t flags,
69 : TALLOC_CTX *mem_ctx,
70 : uint8_t **pdst,
71 : size_t *pdstlen)
72 : {
73 : uint8_t sbuf[12];
74 : struct iovec iov[3];
75 4 : uint8_t *dst = NULL;
76 : ssize_t dst_len;
77 4 : uint8_t *subst_utf16 = NULL;
78 4 : uint8_t *print_utf16 = NULL;
79 4 : size_t subst_len = 0;
80 4 : size_t print_len = 0;
81 4 : bool ret = false;
82 : bool ok;
83 :
84 4 : if (substitute == NULL) {
85 0 : return false;
86 : }
87 4 : if (printname == NULL) {
88 4 : printname = substitute;
89 : }
90 :
91 4 : iov[0] = (struct iovec) { .iov_base = sbuf, .iov_len = sizeof(sbuf), };
92 :
93 4 : ok = convert_string_talloc(
94 : mem_ctx,
95 : CH_UNIX,
96 : CH_UTF16,
97 : substitute,
98 : strlen(substitute),
99 : &subst_utf16,
100 : &subst_len);
101 4 : if (!ok) {
102 0 : goto fail;
103 : }
104 4 : if (subst_len > UINT16_MAX) {
105 0 : goto fail;
106 : }
107 4 : iov[1] = (struct iovec) {
108 : .iov_base = subst_utf16, .iov_len = subst_len,
109 : };
110 :
111 4 : ok = convert_string_talloc(
112 : mem_ctx,
113 : CH_UNIX,
114 : CH_UTF16,
115 : printname,
116 : strlen(printname),
117 : &print_utf16,
118 : &print_len);
119 4 : if (!ok) {
120 0 : goto fail;
121 : }
122 4 : if (print_len > UINT16_MAX) {
123 0 : goto fail;
124 : }
125 4 : iov[2] = (struct iovec) {
126 : .iov_base = print_utf16, .iov_len = print_len,
127 : };
128 :
129 4 : PUSH_LE_U16(sbuf, 0, 0); /* SubstituteNameOffset */
130 4 : PUSH_LE_U16(sbuf, 2, subst_len); /* SubstituteNameLength */
131 4 : PUSH_LE_U16(sbuf, 4, subst_len); /* PrintNameOffset */
132 4 : PUSH_LE_U16(sbuf, 6, print_len); /* PrintNameLength */
133 4 : PUSH_LE_U32(sbuf, 8, flags); /* Flags */
134 :
135 4 : dst_len = reparse_buffer_marshall(
136 : IO_REPARSE_TAG_SYMLINK,
137 : unparsed_path_length,
138 : iov,
139 : ARRAY_SIZE(iov),
140 : NULL,
141 : 0);
142 4 : if (dst_len == -1) {
143 0 : goto fail;
144 : }
145 :
146 4 : dst = talloc_array(mem_ctx, uint8_t, dst_len);
147 4 : if (dst == NULL) {
148 0 : goto fail;
149 : }
150 :
151 4 : reparse_buffer_marshall(
152 : IO_REPARSE_TAG_SYMLINK,
153 : unparsed_path_length,
154 : iov,
155 : ARRAY_SIZE(iov),
156 : dst,
157 : dst_len);
158 :
159 4 : *pdst = dst;
160 4 : *pdstlen = dst_len;
161 4 : ret = true;
162 :
163 4 : fail:
164 4 : TALLOC_FREE(subst_utf16);
165 4 : TALLOC_FREE(print_utf16);
166 4 : return ret;
167 : }
168 :
169 0 : struct symlink_reparse_struct *symlink_reparse_buffer_parse(
170 : TALLOC_CTX *mem_ctx, const uint8_t *src, size_t srclen)
171 : {
172 0 : struct symlink_reparse_struct *result = NULL;
173 : uint16_t reparse_data_length;
174 : uint16_t substitute_name_offset, substitute_name_length;
175 : uint16_t print_name_offset, print_name_length;
176 : bool ok;
177 :
178 0 : if (srclen < 20) {
179 0 : DBG_DEBUG("srclen = %zu, expected >= 20\n", srclen);
180 0 : goto fail;
181 : }
182 0 : if (IVAL(src, 0) != IO_REPARSE_TAG_SYMLINK) {
183 0 : DBG_DEBUG("Got ReparseTag %8.8x, expected %8.8x\n",
184 : IVAL(src, 0),
185 : IO_REPARSE_TAG_SYMLINK);
186 0 : goto fail;
187 : }
188 :
189 0 : reparse_data_length = SVAL(src, 4);
190 0 : substitute_name_offset = SVAL(src, 8);
191 0 : substitute_name_length = SVAL(src, 10);
192 0 : print_name_offset = SVAL(src, 12);
193 0 : print_name_length = SVAL(src, 14);
194 :
195 0 : if (reparse_data_length < 12) {
196 0 : DBG_DEBUG("reparse_data_length = %"PRIu16", expected >= 12\n",
197 : reparse_data_length);
198 0 : goto fail;
199 : }
200 0 : if (smb_buffer_oob(srclen - 8, reparse_data_length, 0)) {
201 0 : DBG_DEBUG("reparse_data_length (%"PRIu16") too large for "
202 : "src_len (%zu)\n",
203 : reparse_data_length,
204 : srclen);
205 0 : goto fail;
206 : }
207 0 : if (smb_buffer_oob(reparse_data_length - 12, substitute_name_offset,
208 : substitute_name_length)) {
209 0 : DBG_DEBUG("substitute_name (%"PRIu16"/%"PRIu16") does not fit "
210 : "in reparse_data_length (%"PRIu16")\n",
211 : substitute_name_offset,
212 : substitute_name_length,
213 : reparse_data_length - 12);
214 0 : goto fail;
215 : }
216 0 : if (smb_buffer_oob(reparse_data_length - 12, print_name_offset,
217 : print_name_length)) {
218 0 : DBG_DEBUG("print_name (%"PRIu16"/%"PRIu16") does not fit in "
219 : "reparse_data_length (%"PRIu16")\n",
220 : print_name_offset,
221 : print_name_length,
222 : reparse_data_length - 12);
223 0 : goto fail;
224 : }
225 :
226 0 : result = talloc_zero(mem_ctx, struct symlink_reparse_struct);
227 0 : if (result == NULL) {
228 0 : DBG_DEBUG("talloc failed\n");
229 0 : goto fail;
230 : }
231 :
232 0 : ok = convert_string_talloc(
233 : result,
234 : CH_UTF16,
235 : CH_UNIX,
236 0 : src + 20 + substitute_name_offset,
237 : substitute_name_length,
238 0 : &result->substitute_name,
239 : NULL);
240 0 : if (!ok) {
241 0 : DBG_DEBUG("convert_string_talloc for substitute_name "
242 : "failed\n");
243 0 : goto fail;
244 : }
245 :
246 0 : ok = convert_string_talloc(
247 : result,
248 : CH_UTF16,
249 : CH_UNIX,
250 0 : src + 20 + print_name_offset,
251 : print_name_length,
252 0 : &result->print_name,
253 : NULL);
254 0 : if (!ok) {
255 0 : DBG_DEBUG("convert_string_talloc for print_name failed\n");
256 0 : goto fail;
257 : }
258 :
259 0 : result->unparsed_path_length = SVAL(src, 6);
260 0 : result->flags = IVAL(src, 16);
261 :
262 0 : return result;
263 0 : fail:
264 0 : TALLOC_FREE(result);
265 0 : return NULL;
266 : }
|