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 0 : urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src)
44 : {
45 0 : int old_length = strlen(src);
46 0 : int i = 0;
47 0 : int err_count = 0;
48 0 : size_t newlen = 1;
49 : char *p, *dest;
50 :
51 0 : if (old_length == 0) {
52 0 : return 0;
53 : }
54 :
55 0 : *pp_dest = NULL;
56 0 : for (i = 0; i < old_length; ) {
57 0 : unsigned char character = src[i++];
58 :
59 0 : if (character == '%') {
60 : 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 0 : newlen++;
73 : }
74 :
75 0 : dest = talloc_array(ctx, char, newlen);
76 0 : if (!dest) {
77 0 : return err_count;
78 : }
79 :
80 0 : err_count = 0;
81 0 : for (p = dest, i = 0; i < old_length; ) {
82 0 : unsigned char character = src[i++];
83 :
84 0 : if (character == '%') {
85 : 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 0 : *p++ = character;
98 : }
99 :
100 0 : *p = '\0';
101 0 : *pp_dest = dest;
102 0 : 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 : 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 0 : 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 : char *s;
222 : const char *p;
223 : char *q, *r;
224 0 : char *workgroup = NULL;
225 : int len;
226 :
227 : /* Ensure these returns are at least valid pointers. */
228 0 : *pp_server = talloc_strdup(ctx, "");
229 0 : *p_port = smbc_getPort(context);
230 0 : *pp_share = talloc_strdup(ctx, "");
231 0 : *pp_path = talloc_strdup(ctx, "");
232 0 : *pp_user = talloc_strdup(ctx, "");
233 0 : *pp_password = talloc_strdup(ctx, "");
234 :
235 0 : if (!*pp_server || !*pp_share || !*pp_path ||
236 0 : !*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 0 : if (pp_workgroup != NULL) {
245 0 : *pp_workgroup =
246 0 : talloc_strdup(ctx, smbc_getWorkgroup(context));
247 : }
248 :
249 0 : if (pp_options) {
250 0 : *pp_options = talloc_strdup(ctx, "");
251 : }
252 0 : s = talloc_strdup(ctx, fname);
253 :
254 : /* see if it has the right prefix */
255 0 : len = strlen(SMBC_PREFIX);
256 0 : if (strncmp(s,SMBC_PREFIX,len) || (s[len] != '/' && s[len] != 0)) {
257 0 : return -1; /* What about no smb: ? */
258 : }
259 :
260 0 : p = s + len;
261 :
262 : /* Watch the test below, we are testing to see if we should exit */
263 :
264 0 : if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
265 0 : DEBUG(1, ("Invalid path (does not begin with smb://"));
266 0 : return -1;
267 : }
268 :
269 0 : p += 2; /* Skip the double slash */
270 :
271 : /* See if any options were specified */
272 0 : 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'", 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 0 : if (*p == '\0') {
286 0 : goto decoding;
287 : }
288 :
289 0 : if (*p == '/') {
290 0 : int wl = strlen(smbc_getWorkgroup(context));
291 :
292 0 : if (wl > 16) {
293 0 : wl = 16;
294 : }
295 :
296 0 : *pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context));
297 0 : if (!*pp_server) {
298 0 : return -1;
299 : }
300 0 : (*pp_server)[wl] = '\0';
301 0 : return 0;
302 : }
303 :
304 : /*
305 : * ok, its for us. Now parse out the server, share etc.
306 : *
307 : * However, we want to parse out [[domain;]user[:password]@] if it
308 : * exists ...
309 : */
310 :
311 : /* check that '@' occurs before '/', if '/' exists at all */
312 0 : q = strchr_m(p, '@');
313 0 : r = strchr_m(p, '/');
314 0 : if (q && (!r || q < r)) {
315 0 : char *userinfo = NULL;
316 : const char *u;
317 :
318 0 : next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
319 0 : if (!userinfo) {
320 0 : return -1;
321 : }
322 0 : u = userinfo;
323 :
324 0 : if (strchr_m(u, ';')) {
325 0 : next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
326 0 : if (!workgroup) {
327 0 : return -1;
328 : }
329 0 : if (pp_workgroup) {
330 0 : *pp_workgroup = workgroup;
331 : }
332 : }
333 :
334 0 : if (strchr_m(u, ':')) {
335 0 : next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
336 0 : if (!*pp_user) {
337 0 : return -1;
338 : }
339 0 : *pp_password = talloc_strdup(ctx, u);
340 0 : if (!*pp_password) {
341 0 : return -1;
342 : }
343 : } else {
344 0 : *pp_user = talloc_strdup(ctx, u);
345 0 : if (!*pp_user) {
346 0 : return -1;
347 : }
348 : }
349 : }
350 :
351 0 : if (!next_token_talloc(ctx, &p, pp_server, "/")) {
352 0 : return -1;
353 : }
354 :
355 : /*
356 : * Does *pp_server contain a ':' ? If so
357 : * this denotes the port.
358 : */
359 0 : q = strchr_m(*pp_server, ':');
360 0 : if (q != NULL) {
361 : long int port;
362 0 : char *endptr = NULL;
363 0 : *q = '\0';
364 0 : q++;
365 0 : if (*q == '\0') {
366 : /* Bad port. */
367 0 : return -1;
368 : }
369 0 : port = strtol(q, &endptr, 10);
370 0 : if (*endptr != '\0') {
371 : /* Bad port. */
372 0 : return -1;
373 : }
374 0 : *p_port = (uint16_t)port;
375 : }
376 :
377 0 : if (*p == (char)0) {
378 0 : goto decoding; /* That's it ... */
379 : }
380 :
381 0 : if (!next_token_talloc(ctx, &p, pp_share, "/")) {
382 0 : return -1;
383 : }
384 :
385 : /*
386 : * Prepend a leading slash if there's a file path, as required by
387 : * NetApp filers.
388 : */
389 0 : if (*p != '\0') {
390 0 : *pp_path = talloc_asprintf(ctx,
391 : "\\%s",
392 : p);
393 : } else {
394 0 : *pp_path = talloc_strdup(ctx, "");
395 : }
396 0 : if (!*pp_path) {
397 0 : return -1;
398 : }
399 0 : string_replace(*pp_path, '/', '\\');
400 :
401 0 : decoding:
402 0 : (void) urldecode_talloc(ctx, pp_path, *pp_path);
403 0 : (void) urldecode_talloc(ctx, pp_server, *pp_server);
404 0 : (void) urldecode_talloc(ctx, pp_share, *pp_share);
405 0 : (void) urldecode_talloc(ctx, pp_user, *pp_user);
406 0 : (void) urldecode_talloc(ctx, pp_password, *pp_password);
407 :
408 0 : if (!workgroup) {
409 0 : workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
410 : }
411 0 : if (!workgroup) {
412 0 : return -1;
413 : }
414 :
415 : /* set the credentials to make DFS work */
416 0 : smbc_set_credentials_with_fallback(context,
417 : workgroup,
418 : *pp_user,
419 : *pp_password);
420 0 : return 0;
421 : }
422 :
|