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/* Used by urldecode_talloc() */ 31static int 32hex2int( unsigned int _char ) 33{ 34 if ( _char >= 'A' && _char <='F') 35 return _char - 'A' + 10; 36 if ( _char >= 'a' && _char <='f') 37 return _char - 'a' + 10; 38 if ( _char >= '0' && _char <='9') 39 return _char - '0'; 40 return -1; 41} 42 43/* 44 * smbc_urldecode() 45 * and urldecode_talloc() (internal fn.) 46 * 47 * Convert strings of %xx to their single character equivalent. Each 'x' must 48 * be a valid hexadecimal digit, or that % sequence is left undecoded. 49 * 50 * dest may, but need not be, the same pointer as src. 51 * 52 * Returns the number of % sequences which could not be converted due to lack 53 * of two following hexadecimal digits. 54 */ 55static int 56urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src) 57{ 58 int old_length = strlen(src); 59 int i = 0; 60 int err_count = 0; 61 size_t newlen = 1; 62 char *p, *dest; 63 64 if (old_length == 0) { 65 return 0; 66 } 67 68 *pp_dest = NULL; 69 for (i = 0; i < old_length; ) { 70 unsigned char character = src[i++]; 71 72 if (character == '%') { 73 int a = i+1 < old_length ? hex2int(src[i]) : -1; 74 int b = i+1 < old_length ? hex2int(src[i+1]) : -1; 75 76 /* Replace valid sequence */ 77 if (a != -1 && b != -1) { 78 /* Replace valid %xx sequence with %dd */ 79 character = (a * 16) + b; 80 if (character == '\0') { 81 break; /* Stop at %00 */ 82 } 83 i += 2; 84 } else { 85 err_count++; 86 } 87 } 88 newlen++; 89 } 90 91 dest = TALLOC_ARRAY(ctx, char, newlen); 92 if (!dest) { 93 return err_count; 94 } 95 96 err_count = 0; 97 for (p = dest, i = 0; i < old_length; ) { 98 unsigned char character = src[i++]; 99 100 if (character == '%') { 101 int a = i+1 < old_length ? hex2int(src[i]) : -1; 102 int b = i+1 < old_length ? hex2int(src[i+1]) : -1; 103 104 /* Replace valid sequence */ 105 if (a != -1 && b != -1) { 106 /* Replace valid %xx sequence with %dd */ 107 character = (a * 16) + b; 108 if (character == '\0') { 109 break; /* Stop at %00 */ 110 } 111 i += 2; 112 } else { 113 err_count++; 114 } 115 } 116 *p++ = character; 117 } 118 119 *p = '\0'; 120 *pp_dest = dest; 121 return err_count; 122} 123 124int 125smbc_urldecode(char *dest, 126 char *src, 127 size_t max_dest_len) 128{ 129 TALLOC_CTX *frame = talloc_stackframe(); 130 char *pdest; 131 int ret = urldecode_talloc(frame, &pdest, src); 132 133 if (pdest) { 134 strlcpy(dest, pdest, max_dest_len); 135 } 136 TALLOC_FREE(frame); 137 return ret; 138} 139 140/* 141 * smbc_urlencode() 142 * 143 * Convert any characters not specifically allowed in a URL into their %xx 144 * equivalent. 145 * 146 * Returns the remaining buffer length. 147 */ 148int 149smbc_urlencode(char *dest, 150 char *src, 151 int max_dest_len) 152{ 153 char hex[] = "0123456789ABCDEF"; 154 155 for (; *src != '\0' && max_dest_len >= 3; src++) { 156 157 if ((*src < '0' && 158 *src != '-' && 159 *src != '.') || 160 (*src > '9' && 161 *src < 'A') || 162 (*src > 'Z' && 163 *src < 'a' && 164 *src != '_') || 165 (*src > 'z')) { 166 *dest++ = '%'; 167 *dest++ = hex[(*src >> 4) & 0x0f]; 168 *dest++ = hex[*src & 0x0f]; 169 max_dest_len -= 3; 170 } else { 171 *dest++ = *src; 172 max_dest_len--; 173 } 174 } 175 176 *dest++ = '\0'; 177 max_dest_len--; 178 179 return max_dest_len; 180} 181 182/* 183 * Function to parse a path and turn it into components 184 * 185 * The general format of an SMB URI is explain in Christopher Hertel's CIFS 186 * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the 187 * general format ("smb:" only; we do not look for "cifs:"). 188 * 189 * 190 * We accept: 191 * smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options] 192 * 193 * Meaning of URLs: 194 * 195 * smb:// Show all workgroups. 196 * 197 * The method of locating the list of workgroups varies 198 * depending upon the setting of the context variable 199 * context->options.browse_max_lmb_count. This value 200 * determines the maximum number of local master browsers to 201 * query for the list of workgroups. In order to ensure that 202 * a complete list of workgroups is obtained, all master 203 * browsers must be queried, but if there are many 204 * workgroups, the time spent querying can begin to add up. 205 * For small networks (not many workgroups), it is suggested 206 * that this variable be set to 0, indicating query all local 207 * master browsers. When the network has many workgroups, a 208 * reasonable setting for this variable might be around 3. 209 * 210 * smb://name/ if name<1D> or name<1B> exists, list servers in 211 * workgroup, else, if name<20> exists, list all shares 212 * for server ... 213 * 214 * If "options" are provided, this function returns the entire option list as a 215 * string, for later parsing by the caller. Note that currently, no options 216 * are supported. 217 */ 218 219#define SMBC_PREFIX "smb:" 220 221int 222SMBC_parse_path(TALLOC_CTX *ctx, 223 SMBCCTX *context, 224 const char *fname, 225 char **pp_workgroup, 226 char **pp_server, 227 char **pp_share, 228 char **pp_path, 229 char **pp_user, 230 char **pp_password, 231 char **pp_options) 232{ 233 char *s; 234 const char *p; 235 char *q, *r; 236 char *workgroup = NULL; 237 int len; 238 239 /* Ensure these returns are at least valid pointers. */ 240 *pp_server = talloc_strdup(ctx, ""); 241 *pp_share = talloc_strdup(ctx, ""); 242 *pp_path = talloc_strdup(ctx, ""); 243 *pp_user = talloc_strdup(ctx, ""); 244 *pp_password = talloc_strdup(ctx, ""); 245 246 if (!*pp_server || !*pp_share || !*pp_path || 247 !*pp_user || !*pp_password) { 248 return -1; 249 } 250 251 /* 252 * Assume we wont find an authentication domain to parse, so default 253 * to the workgroup in the provided context. 254 */ 255 if (pp_workgroup != NULL) { 256 *pp_workgroup = 257 talloc_strdup(ctx, smbc_getWorkgroup(context)); 258 } 259 260 if (pp_options) { 261 *pp_options = talloc_strdup(ctx, ""); 262 } 263 s = talloc_strdup(ctx, fname); 264 265 /* see if it has the right prefix */ 266 len = strlen(SMBC_PREFIX); 267 if (strncmp(s,SMBC_PREFIX,len) || (s[len] != '/' && s[len] != 0)) { 268 return -1; /* What about no smb: ? */ 269 } 270 271 p = s + len; 272 273 /* Watch the test below, we are testing to see if we should exit */ 274 275 if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) { 276 DEBUG(1, ("Invalid path (does not begin with smb://")); 277 return -1; 278 } 279 280 p += 2; /* Skip the double slash */ 281 282//- JerryLin Modify 283#if 1 284 /* See if any options were specified */ 285 if ((q = strrchr(p, '?')) != NULL ) { 286 /* There are options. Null terminate here and point to them */ 287 *q++ = '\0'; 288 289 DEBUG(4, ("Found options '%s'", q)); 290 291 /* Copy the options */ 292 if (pp_options && *pp_options != NULL) { 293 TALLOC_FREE(*pp_options); 294 *pp_options = talloc_strdup(ctx, q); 295 } 296 } 297#else 298 /* See if any options were specified */ 299 if ((q = strrchr(p, '?')) != NULL ) { 300 /* There are options. Null terminate here and point to them */ 301 *q++ = '\0'; 302 303 DEBUG(4, ("Found options '%s'", q)); 304 305 //- 20111221 Jerry add 306 char* option = talloc_strdup(ctx, q); 307 char * pch; 308 pch = strtok(option, "&"); 309 while(pch!=NULL){ 310 if(strncmp(pch, "len=", 4)==0){ 311 char* r = strchr_m(pch, '/'); 312 int index = r- pch; 313 //fprintf(stderr, "parser index = %d\n", index); 314 int len = strlen(pch)-4; 315 316 char *temp = talloc_strndup(ctx, pch+4, len); 317 userinfo_len = atoi(temp); 318 //fprintf(stderr, "parser len = %d\n", userinfo_len); 319 } 320 321 pch = strtok( NULL, "&" ); 322 } 323 324 /* Copy the options */ 325 if (pp_options && *pp_options != NULL) { 326 TALLOC_FREE(*pp_options); 327 *pp_options = talloc_strdup(ctx, q); 328 } 329 } 330#endif 331 332 if (*p == '\0') { 333 goto decoding; 334 } 335 336 if (*p == '/') { 337 int wl = strlen(smbc_getWorkgroup(context)); 338 339 if (wl > 16) { 340 wl = 16; 341 } 342 343 *pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context)); 344 if (!*pp_server) { 345 return -1; 346 } 347 (*pp_server)[wl] = '\0'; 348 return 0; 349 } 350 351 /* 352 * ok, its for us. Now parse out the server, share etc. 353 * 354 * However, we want to parse out [[domain;]user[:password]@] if it 355 * exists ... 356 */ 357 /* check that '@' occurs before '/', if '/' exists at all */ 358#if 1 359 360 //- 20111221 Jerry add 361 int userinfo_len = -1; 362 int index = 0; 363 364 q = strchr_m(p, '@'); 365 r = strchr_m(p, '/'); 366 367 while (q&& (!r || q < r)) 368 { 369 index = q-p+1; 370 //fprintf (stderr, "found at %d\n", index); 371 q = strchr_m(q+1, '@'); 372 } 373 374 if(index>0) 375 userinfo_len = index - 1; 376 377 if(userinfo_len!=-1){ 378 char *userinfo = NULL; 379 const char *u; 380 381 userinfo = talloc_strndup(ctx, p, userinfo_len); 382 383 if (!userinfo) { 384 return -1; 385 } 386 387 //fprintf(stderr, "Libsmb_path.c->SMBC_parse_path: userinfo=%s\n", userinfo); 388 389 u = userinfo; 390 391 if (strchr_m(u, ';')) { 392 next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";"); 393 if (!workgroup) { 394 return -1; 395 } 396 if (pp_workgroup) { 397 *pp_workgroup = workgroup; 398 } 399 } 400 401 if (strchr_m(u, ':')) { 402 next_token_no_ltrim_talloc(ctx, &u, pp_user, ":"); 403 if (!*pp_user) { 404 return -1; 405 } 406 *pp_password = talloc_strdup(ctx, u); 407 if (!*pp_password) { 408 return -1; 409 } 410 } else { 411 *pp_user = talloc_strdup(ctx, u); 412 if (!*pp_user) { 413 return -1; 414 } 415 } 416 417 p = p + userinfo_len + 1; 418 419 //fprintf(stderr, "pp_user=%s, pp_password=%s\n", *pp_user, *pp_password); 420 } 421 422#else 423 424 /* check that '@' occurs before '/', if '/' exists at all */ 425 q = strchr_m(p, '@'); 426 r = strchr_m(p, '/'); 427 428 if (q && (!r || q < r)) { 429 char *userinfo = NULL; 430 const char *u; 431 432 next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@"); 433 if (!userinfo) { 434 return -1; 435 } 436 u = userinfo; 437 438 if (strchr_m(u, ';')) { 439 next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";"); 440 if (!workgroup) { 441 return -1; 442 } 443 if (pp_workgroup) { 444 *pp_workgroup = workgroup; 445 } 446 } 447 448 if (strchr_m(u, ':')) { 449 next_token_no_ltrim_talloc(ctx, &u, pp_user, ":"); 450 if (!*pp_user) { 451 return -1; 452 } 453 *pp_password = talloc_strdup(ctx, u); 454 if (!*pp_password) { 455 return -1; 456 } 457 } else { 458 *pp_user = talloc_strdup(ctx, u); 459 if (!*pp_user) { 460 return -1; 461 } 462 } 463 464 fprintf(stderr, "pp_user=%s, pp_password=%s\n", *pp_user, *pp_password); 465 466 } 467#endif 468 469 if (!next_token_talloc(ctx, &p, pp_server, "/")) { 470 return -1; 471 } 472 473 if (*p == (char)0) { 474 goto decoding; /* That's it ... */ 475 } 476 477 if (!next_token_talloc(ctx, &p, pp_share, "/")) { 478 return -1; 479 } 480 481 /* 482 * Prepend a leading slash if there's a file path, as required by 483 * NetApp filers. 484 */ 485 if (*p != '\0') { 486 *pp_path = talloc_asprintf(ctx, 487 "\\%s", 488 p); 489 } else { 490 *pp_path = talloc_strdup(ctx, ""); 491 } 492 if (!*pp_path) { 493 return -1; 494 } 495 string_replace(*pp_path, '/', '\\'); 496 497decoding: 498 499 (void) urldecode_talloc(ctx, pp_path, *pp_path); 500 (void) urldecode_talloc(ctx, pp_server, *pp_server); 501 (void) urldecode_talloc(ctx, pp_share, *pp_share); 502 (void) urldecode_talloc(ctx, pp_user, *pp_user); 503 (void) urldecode_talloc(ctx, pp_password, *pp_password); 504 505 if (!workgroup) { 506 workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context)); 507 } 508 if (!workgroup) { 509 return -1; 510 } 511 512 /* set the credentials to make DFS work */ 513 smbc_set_credentials_with_fallback(context, 514 workgroup, 515 *pp_user, 516 *pp_password); 517 518 return 0; 519} 520 521