Line data Source code
1 : /*
2 : Unix SMB/Netbios implementation.
3 : SMB client library implementation
4 : Copyright (C) Andrew Tridgell 1998
5 : Copyright (C) Richard Sharpe 2000, 2002
6 : Copyright (C) John Terpstra 2000
7 : Copyright (C) Tom Jansen (Ninja ISD) 2002
8 : Copyright (C) Derrell Lipman 2003-2008
9 : Copyright (C) Jeremy Allison 2007, 2008
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include "includes.h"
26 : #include "libsmbclient.h"
27 : #include "libsmb_internal.h"
28 :
29 :
30 : /*
31 : * smbc_urldecode()
32 : * and urldecode_talloc() (internal fn.)
33 : *
34 : * Convert strings of %xx to their single character equivalent. Each 'x' must
35 : * be a valid hexadecimal digit, or that % sequence is left undecoded.
36 : *
37 : * dest may, but need not be, the same pointer as src.
38 : *
39 : * Returns the number of % sequences which could not be converted due to lack
40 : * of two following hexadecimal digits.
41 : */
42 : static int
43 8215 : urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src)
44 : {
45 8215 : int old_length = strlen(src);
46 8215 : int i = 0;
47 8215 : int err_count = 0;
48 8215 : size_t newlen = 1;
49 0 : char *p, *dest;
50 :
51 8215 : if (old_length == 0) {
52 360 : return 0;
53 : }
54 :
55 7855 : *pp_dest = NULL;
56 104501 : for (i = 0; i < old_length; ) {
57 96646 : unsigned char character = src[i++];
58 :
59 96646 : if (character == '%') {
60 0 : uint8_t v;
61 0 : bool ok = hex_byte(&src[i], &v);
62 0 : if (ok) {
63 0 : character = v;
64 0 : if (character == '\0') {
65 0 : break; /* Stop at %00 */
66 : }
67 0 : i += 2;
68 : } else {
69 0 : err_count++;
70 : }
71 : }
72 96646 : newlen++;
73 : }
74 :
75 7855 : dest = talloc_array(ctx, char, newlen);
76 7855 : if (!dest) {
77 0 : return err_count;
78 : }
79 :
80 7855 : err_count = 0;
81 104501 : for (p = dest, i = 0; i < old_length; ) {
82 96646 : unsigned char character = src[i++];
83 :
84 96646 : if (character == '%') {
85 0 : uint8_t v;
86 0 : bool ok = hex_byte(&src[i], &v);
87 0 : if (ok) {
88 0 : character = v;
89 0 : if (character == '\0') {
90 0 : break; /* Stop at %00 */
91 : }
92 0 : i += 2;
93 : } else {
94 0 : err_count++;
95 : }
96 : }
97 96646 : *p++ = character;
98 : }
99 :
100 7855 : *p = '\0';
101 7855 : *pp_dest = dest;
102 7855 : return err_count;
103 : }
104 :
105 : int
106 0 : smbc_urldecode(char *dest,
107 : char *src,
108 : size_t max_dest_len)
109 : {
110 0 : TALLOC_CTX *frame = talloc_stackframe();
111 0 : char *pdest;
112 0 : int ret = urldecode_talloc(frame, &pdest, src);
113 :
114 0 : if (pdest) {
115 0 : strlcpy(dest, pdest, max_dest_len);
116 : }
117 0 : TALLOC_FREE(frame);
118 0 : return ret;
119 : }
120 :
121 : /*
122 : * smbc_urlencode()
123 : *
124 : * Convert any characters not specifically allowed in a URL into their %xx
125 : * equivalent.
126 : *
127 : * Returns the remaining buffer length.
128 : */
129 : int
130 0 : smbc_urlencode(char *dest,
131 : char *src,
132 : int max_dest_len)
133 : {
134 0 : char hex[] = "0123456789ABCDEF";
135 :
136 0 : for (; *src != '\0' && max_dest_len >= 3; src++) {
137 :
138 0 : if ((*src < '0' &&
139 0 : *src != '-' &&
140 0 : *src != '.') ||
141 0 : (*src > '9' &&
142 0 : *src < 'A') ||
143 0 : (*src > 'Z' &&
144 0 : *src < 'a' &&
145 0 : *src != '_') ||
146 0 : (*src > 'z')) {
147 0 : *dest++ = '%';
148 0 : *dest++ = hex[(*src >> 4) & 0x0f];
149 0 : *dest++ = hex[*src & 0x0f];
150 0 : max_dest_len -= 3;
151 : } else {
152 0 : *dest++ = *src;
153 0 : max_dest_len--;
154 : }
155 : }
156 :
157 0 : if (max_dest_len <= 0) {
158 : /* Ensure we return -1 if no null termination. */
159 0 : return -1;
160 : }
161 :
162 0 : *dest++ = '\0';
163 0 : max_dest_len--;
164 :
165 0 : return max_dest_len;
166 : }
167 :
168 : /*
169 : * Function to parse a path and turn it into components
170 : *
171 : * The general format of an SMB URI is explain in Christopher Hertel's CIFS
172 : * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the
173 : * general format ("smb:" only; we do not look for "cifs:").
174 : *
175 : *
176 : * We accept:
177 : * smb://[[[domain;]user[:password]@]server[:port][/share[/path[/file]]]]
178 : * [?options]
179 : *
180 : * Meaning of URLs:
181 : *
182 : * smb:// Show all workgroups.
183 : *
184 : * The method of locating the list of workgroups varies
185 : * depending upon the setting of the context variable
186 : * context->options.browse_max_lmb_count. This value
187 : * determines the maximum number of local master browsers to
188 : * query for the list of workgroups. In order to ensure that
189 : * a complete list of workgroups is obtained, all master
190 : * browsers must be queried, but if there are many
191 : * workgroups, the time spent querying can begin to add up.
192 : * For small networks (not many workgroups), it is suggested
193 : * that this variable be set to 0, indicating query all local
194 : * master browsers. When the network has many workgroups, a
195 : * reasonable setting for this variable might be around 3.
196 : *
197 : * smb://name/ if name<1D> or name<1B> exists, list servers in
198 : * workgroup, else, if name<20> exists, list all shares
199 : * for server ...
200 : *
201 : * If "options" are provided, this function returns the entire option list as a
202 : * string, for later parsing by the caller. Note that currently, no options
203 : * are supported.
204 : */
205 :
206 : #define SMBC_PREFIX "smb:"
207 :
208 : int
209 1695 : SMBC_parse_path(TALLOC_CTX *ctx,
210 : SMBCCTX *context,
211 : const char *fname,
212 : char **pp_workgroup,
213 : char **pp_server,
214 : uint16_t *p_port,
215 : char **pp_share,
216 : char **pp_path,
217 : char **pp_user,
218 : char **pp_password,
219 : char **pp_options)
220 : {
221 0 : char *s;
222 0 : const char *p;
223 0 : char *q, *r;
224 1695 : char *workgroup = NULL;
225 0 : int len;
226 :
227 : /* Ensure these returns are at least valid pointers. */
228 1695 : *pp_server = talloc_strdup(ctx, "");
229 1695 : *p_port = smbc_getPort(context);
230 1695 : *pp_share = talloc_strdup(ctx, "");
231 1695 : *pp_path = talloc_strdup(ctx, "");
232 1695 : *pp_user = talloc_strdup(ctx, "");
233 1695 : *pp_password = talloc_strdup(ctx, "");
234 :
235 1695 : if (!*pp_server || !*pp_share || !*pp_path ||
236 1695 : !*pp_user || !*pp_password) {
237 0 : return -1;
238 : }
239 :
240 : /*
241 : * Assume we won't find an authentication domain to parse, so default
242 : * to the workgroup in the provided context.
243 : */
244 1695 : if (pp_workgroup != NULL) {
245 1623 : *pp_workgroup =
246 1623 : talloc_strdup(ctx, smbc_getWorkgroup(context));
247 : }
248 :
249 1695 : if (pp_options) {
250 247 : *pp_options = talloc_strdup(ctx, "");
251 : }
252 1695 : s = talloc_strdup(ctx, fname);
253 :
254 : /* see if it has the right prefix */
255 1695 : len = strlen(SMBC_PREFIX);
256 1695 : if (strncmp(s,SMBC_PREFIX,len) || (s[len] != '/' && s[len] != 0)) {
257 40 : return -1; /* What about no smb: ? */
258 : }
259 :
260 1655 : p = s + len;
261 :
262 : /* Watch the test below, we are testing to see if we should exit */
263 :
264 1655 : if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
265 8 : DEBUG(1, ("Invalid path (does not begin with smb://)\n"));
266 8 : return -1;
267 : }
268 :
269 1647 : p += 2; /* Skip the double slash */
270 :
271 : /* See if any options were specified */
272 1647 : if ((q = strrchr(p, '?')) != NULL ) {
273 : /* There are options. Null terminate here and point to them */
274 0 : *q++ = '\0';
275 :
276 0 : DEBUG(4, ("Found options '%s'\n", q));
277 :
278 : /* Copy the options */
279 0 : if (pp_options && *pp_options != NULL) {
280 0 : TALLOC_FREE(*pp_options);
281 0 : *pp_options = talloc_strdup(ctx, q);
282 : }
283 : }
284 :
285 1647 : if (*p == '\0') {
286 4 : goto decoding;
287 : }
288 :
289 1643 : if (*p == '/') {
290 4 : *pp_server = talloc_strndup(
291 : ctx, smbc_getWorkgroup(context), 16);
292 4 : if (!*pp_server) {
293 0 : return -1;
294 : }
295 4 : return 0;
296 : }
297 :
298 : /*
299 : * ok, its for us. Now parse out the server, share etc.
300 : *
301 : * However, we want to parse out [[domain;]user[:password]@] if it
302 : * exists ...
303 : */
304 :
305 : /* check that '@' occurs before '/', if '/' exists at all */
306 1639 : q = strchr_m(p, '@');
307 1639 : r = strchr_m(p, '/');
308 1639 : if (q && (!r || q < r)) {
309 1493 : char *userinfo = NULL;
310 0 : const char *u;
311 :
312 1493 : next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
313 1493 : if (!userinfo) {
314 0 : return -1;
315 : }
316 1493 : u = userinfo;
317 :
318 1493 : if (strchr_m(u, ';')) {
319 8 : next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
320 8 : if (!workgroup) {
321 0 : return -1;
322 : }
323 8 : if (pp_workgroup) {
324 4 : *pp_workgroup = workgroup;
325 : }
326 : }
327 :
328 1493 : if (strchr_m(u, ':')) {
329 1489 : next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
330 1489 : if (!*pp_user) {
331 0 : return -1;
332 : }
333 1489 : *pp_password = talloc_strdup(ctx, u);
334 1489 : if (!*pp_password) {
335 0 : return -1;
336 : }
337 : } else {
338 4 : *pp_user = talloc_strdup(ctx, u);
339 4 : if (!*pp_user) {
340 0 : return -1;
341 : }
342 : }
343 : }
344 :
345 1639 : if (!next_token_talloc(ctx, &p, pp_server, "/")) {
346 0 : return -1;
347 : }
348 :
349 : /*
350 : * Does *pp_server contain a ':' ? If so
351 : * this denotes the port.
352 : */
353 1639 : q = strchr_m(*pp_server, ':');
354 1639 : if (q != NULL) {
355 0 : long int port;
356 0 : char *endptr = NULL;
357 0 : *q = '\0';
358 0 : q++;
359 0 : if (*q == '\0') {
360 : /* Bad port. */
361 0 : return -1;
362 : }
363 0 : port = strtol(q, &endptr, 10);
364 0 : if (*endptr != '\0') {
365 : /* Bad port. */
366 0 : return -1;
367 : }
368 0 : *p_port = (uint16_t)port;
369 : }
370 :
371 1639 : if (*p == (char)0) {
372 12 : goto decoding; /* That's it ... */
373 : }
374 :
375 1627 : if (!next_token_talloc(ctx, &p, pp_share, "/")) {
376 0 : return -1;
377 : }
378 :
379 : /*
380 : * Prepend a leading slash if there's a file path, as required by
381 : * NetApp filers.
382 : */
383 1627 : if (*p != '\0') {
384 1607 : *pp_path = talloc_asprintf(ctx,
385 : "\\%s",
386 : p);
387 : } else {
388 20 : *pp_path = talloc_strdup(ctx, "");
389 : }
390 1627 : if (!*pp_path) {
391 0 : return -1;
392 : }
393 1627 : string_replace(*pp_path, '/', '\\');
394 :
395 1643 : decoding:
396 1643 : (void) urldecode_talloc(ctx, pp_path, *pp_path);
397 1643 : (void) urldecode_talloc(ctx, pp_server, *pp_server);
398 1643 : (void) urldecode_talloc(ctx, pp_share, *pp_share);
399 1643 : (void) urldecode_talloc(ctx, pp_user, *pp_user);
400 1643 : (void) urldecode_talloc(ctx, pp_password, *pp_password);
401 :
402 1643 : if (!workgroup) {
403 1635 : workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
404 : }
405 1643 : if (!workgroup) {
406 0 : return -1;
407 : }
408 :
409 : /* set the credentials to make DFS work */
410 1643 : smbc_set_credentials_with_fallback(context,
411 : workgroup,
412 : *pp_user,
413 : *pp_password);
414 1643 : return 0;
415 : }
416 :
|