51570b563e44609c142aefb4828d19ea3e7c081e
[samba.git] / libcli / smb / reparse_symlink.c
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 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         ssize_t reparse_data_length = iov_buflen(iov, iovlen);
40         size_t needed;
41
42         if (reparse_data_length == -1) {
43                 return -1;
44         }
45         if (reparse_data_length > UINT16_MAX) {
46                 return -1;
47         }
48
49         needed = reparse_data_length + 8;
50         if (needed < reparse_data_length) {
51                 return -1;
52         }
53
54         if (buflen >= needed) {
55                 PUSH_LE_U32(buf, 0, reparse_tag);
56                 PUSH_LE_U16(buf, 4, reparse_data_length);
57                 PUSH_LE_U16(buf, 6, reserved);
58                 iov_buf(iov, iovlen, buf+8, buflen-8);
59         }
60
61         return needed;
62 }
63
64 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         uint8_t *dst = NULL;
76         ssize_t dst_len;
77         uint8_t *subst_utf16 = NULL;
78         uint8_t *print_utf16 = NULL;
79         size_t subst_len = 0;
80         size_t print_len = 0;
81         bool ret = false;
82         bool ok;
83
84         if (substitute == NULL) {
85                 return false;
86         }
87         if (printname == NULL) {
88                 printname = substitute;
89         }
90
91         iov[0] = (struct iovec) { .iov_base = sbuf, .iov_len = sizeof(sbuf), };
92
93         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         if (!ok) {
102                 goto fail;
103         }
104         if (subst_len > UINT16_MAX) {
105                 goto fail;
106         }
107         iov[1] = (struct iovec) {
108                 .iov_base = subst_utf16, .iov_len = subst_len,
109         };
110
111         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         if (!ok) {
120                 goto fail;
121         }
122         if (print_len > UINT16_MAX) {
123                 goto fail;
124         }
125         iov[2] = (struct iovec) {
126                 .iov_base = print_utf16, .iov_len = print_len,
127         };
128
129         PUSH_LE_U16(sbuf, 0, 0);         /* SubstituteNameOffset */
130         PUSH_LE_U16(sbuf, 2, subst_len); /* SubstituteNameLength */
131         PUSH_LE_U16(sbuf, 4, subst_len); /* PrintNameOffset */
132         PUSH_LE_U16(sbuf, 6, print_len); /* PrintNameLength */
133         PUSH_LE_U32(sbuf, 8, flags);     /* Flags */
134
135         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         if (dst_len == -1) {
143                 goto fail;
144         }
145
146         dst = talloc_array(mem_ctx, uint8_t, dst_len);
147         if (dst == NULL) {
148                 goto fail;
149         }
150
151         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         *pdst = dst;
160         *pdstlen = dst_len;
161         ret = true;
162
163 fail:
164         TALLOC_FREE(subst_utf16);
165         TALLOC_FREE(print_utf16);
166         return ret;
167 }
168
169 struct symlink_reparse_struct *symlink_reparse_buffer_parse(
170         TALLOC_CTX *mem_ctx, const uint8_t *src, size_t srclen)
171 {
172         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         if (srclen < 20) {
179                 DBG_DEBUG("srclen = %zu, expected >= 20\n", srclen);
180                 goto fail;
181         }
182         if (IVAL(src, 0) != IO_REPARSE_TAG_SYMLINK) {
183                 DBG_DEBUG("Got ReparseTag %8.8x, expected %8.8x\n",
184                           IVAL(src, 0),
185                           IO_REPARSE_TAG_SYMLINK);
186                 goto fail;
187         }
188
189         reparse_data_length     = SVAL(src, 4);
190         substitute_name_offset  = SVAL(src, 8);
191         substitute_name_length  = SVAL(src, 10);
192         print_name_offset       = SVAL(src, 12);
193         print_name_length       = SVAL(src, 14);
194
195         if (reparse_data_length < 12) {
196                 DBG_DEBUG("reparse_data_length = %"PRIu16", expected >= 12\n",
197                           reparse_data_length);
198                 goto fail;
199         }
200         if (smb_buffer_oob(srclen - 8, reparse_data_length, 0)) {
201                 DBG_DEBUG("reparse_data_length (%"PRIu16") too large for "
202                            "src_len (%zu)\n",
203                           reparse_data_length,
204                           srclen);
205                 goto fail;
206         }
207         if (smb_buffer_oob(reparse_data_length - 12, substitute_name_offset,
208                            substitute_name_length)) {
209                 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                 goto fail;
215         }
216         if (smb_buffer_oob(reparse_data_length - 12, print_name_offset,
217                            print_name_length)) {
218                 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                 goto fail;
224         }
225
226         result = talloc_zero(mem_ctx, struct symlink_reparse_struct);
227         if (result == NULL) {
228                 DBG_DEBUG("talloc failed\n");
229                 goto fail;
230         }
231
232         ok = convert_string_talloc(
233                 result,
234                 CH_UTF16,
235                 CH_UNIX,
236                 src + 20 + substitute_name_offset,
237                 substitute_name_length,
238                 &result->substitute_name,
239                 NULL);
240         if (!ok) {
241                 DBG_DEBUG("convert_string_talloc for substitute_name "
242                           "failed\n");
243                 goto fail;
244         }
245
246         ok = convert_string_talloc(
247                 result,
248                 CH_UTF16,
249                 CH_UNIX,
250                 src + 20 + print_name_offset,
251                 print_name_length,
252                 &result->print_name,
253                 NULL);
254         if (!ok) {
255                 DBG_DEBUG("convert_string_talloc for print_name failed\n");
256                 goto fail;
257         }
258
259         result->unparsed_path_length = SVAL(src, 6);
260         result->flags = IVAL(src, 16);
261
262         return result;
263 fail:
264         TALLOC_FREE(result);
265         return NULL;
266 }