summaryrefslogtreecommitdiffstats
path: root/source3/libsmb/libsmb_path.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source3/libsmb/libsmb_path.c
parentInitial commit. (diff)
downloadsamba-upstream.tar.xz
samba-upstream.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/libsmb/libsmb_path.c')
-rw-r--r--source3/libsmb/libsmb_path.c422
1 files changed, 422 insertions, 0 deletions
diff --git a/source3/libsmb/libsmb_path.c b/source3/libsmb/libsmb_path.c
new file mode 100644
index 0000000..73dc374
--- /dev/null
+++ b/source3/libsmb/libsmb_path.c
@@ -0,0 +1,422 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * smbc_urldecode()
+ * and urldecode_talloc() (internal fn.)
+ *
+ * Convert strings of %xx to their single character equivalent. Each 'x' must
+ * be a valid hexadecimal digit, or that % sequence is left undecoded.
+ *
+ * dest may, but need not be, the same pointer as src.
+ *
+ * Returns the number of % sequences which could not be converted due to lack
+ * of two following hexadecimal digits.
+ */
+static int
+urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src)
+{
+ int old_length = strlen(src);
+ int i = 0;
+ int err_count = 0;
+ size_t newlen = 1;
+ char *p, *dest;
+
+ if (old_length == 0) {
+ return 0;
+ }
+
+ *pp_dest = NULL;
+ for (i = 0; i < old_length; ) {
+ unsigned char character = src[i++];
+
+ if (character == '%') {
+ uint8_t v;
+ bool ok = hex_byte(&src[i], &v);
+ if (ok) {
+ character = v;
+ if (character == '\0') {
+ break; /* Stop at %00 */
+ }
+ i += 2;
+ } else {
+ err_count++;
+ }
+ }
+ newlen++;
+ }
+
+ dest = talloc_array(ctx, char, newlen);
+ if (!dest) {
+ return err_count;
+ }
+
+ err_count = 0;
+ for (p = dest, i = 0; i < old_length; ) {
+ unsigned char character = src[i++];
+
+ if (character == '%') {
+ uint8_t v;
+ bool ok = hex_byte(&src[i], &v);
+ if (ok) {
+ character = v;
+ if (character == '\0') {
+ break; /* Stop at %00 */
+ }
+ i += 2;
+ } else {
+ err_count++;
+ }
+ }
+ *p++ = character;
+ }
+
+ *p = '\0';
+ *pp_dest = dest;
+ return err_count;
+}
+
+int
+smbc_urldecode(char *dest,
+ char *src,
+ size_t max_dest_len)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *pdest;
+ int ret = urldecode_talloc(frame, &pdest, src);
+
+ if (pdest) {
+ strlcpy(dest, pdest, max_dest_len);
+ }
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+/*
+ * smbc_urlencode()
+ *
+ * Convert any characters not specifically allowed in a URL into their %xx
+ * equivalent.
+ *
+ * Returns the remaining buffer length.
+ */
+int
+smbc_urlencode(char *dest,
+ char *src,
+ int max_dest_len)
+{
+ char hex[] = "0123456789ABCDEF";
+
+ for (; *src != '\0' && max_dest_len >= 3; src++) {
+
+ if ((*src < '0' &&
+ *src != '-' &&
+ *src != '.') ||
+ (*src > '9' &&
+ *src < 'A') ||
+ (*src > 'Z' &&
+ *src < 'a' &&
+ *src != '_') ||
+ (*src > 'z')) {
+ *dest++ = '%';
+ *dest++ = hex[(*src >> 4) & 0x0f];
+ *dest++ = hex[*src & 0x0f];
+ max_dest_len -= 3;
+ } else {
+ *dest++ = *src;
+ max_dest_len--;
+ }
+ }
+
+ if (max_dest_len <= 0) {
+ /* Ensure we return -1 if no null termination. */
+ return -1;
+ }
+
+ *dest++ = '\0';
+ max_dest_len--;
+
+ return max_dest_len;
+}
+
+/*
+ * Function to parse a path and turn it into components
+ *
+ * The general format of an SMB URI is explain in Christopher Hertel's CIFS
+ * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the
+ * general format ("smb:" only; we do not look for "cifs:").
+ *
+ *
+ * We accept:
+ * smb://[[[domain;]user[:password]@]server[:port][/share[/path[/file]]]]
+ * [?options]
+ *
+ * Meaning of URLs:
+ *
+ * smb:// Show all workgroups.
+ *
+ * The method of locating the list of workgroups varies
+ * depending upon the setting of the context variable
+ * context->options.browse_max_lmb_count. This value
+ * determines the maximum number of local master browsers to
+ * query for the list of workgroups. In order to ensure that
+ * a complete list of workgroups is obtained, all master
+ * browsers must be queried, but if there are many
+ * workgroups, the time spent querying can begin to add up.
+ * For small networks (not many workgroups), it is suggested
+ * that this variable be set to 0, indicating query all local
+ * master browsers. When the network has many workgroups, a
+ * reasonable setting for this variable might be around 3.
+ *
+ * smb://name/ if name<1D> or name<1B> exists, list servers in
+ * workgroup, else, if name<20> exists, list all shares
+ * for server ...
+ *
+ * If "options" are provided, this function returns the entire option list as a
+ * string, for later parsing by the caller. Note that currently, no options
+ * are supported.
+ */
+
+#define SMBC_PREFIX "smb:"
+
+int
+SMBC_parse_path(TALLOC_CTX *ctx,
+ SMBCCTX *context,
+ const char *fname,
+ char **pp_workgroup,
+ char **pp_server,
+ uint16_t *p_port,
+ char **pp_share,
+ char **pp_path,
+ char **pp_user,
+ char **pp_password,
+ char **pp_options)
+{
+ char *s;
+ const char *p;
+ char *q, *r;
+ char *workgroup = NULL;
+ int len;
+
+ /* Ensure these returns are at least valid pointers. */
+ *pp_server = talloc_strdup(ctx, "");
+ *p_port = smbc_getPort(context);
+ *pp_share = talloc_strdup(ctx, "");
+ *pp_path = talloc_strdup(ctx, "");
+ *pp_user = talloc_strdup(ctx, "");
+ *pp_password = talloc_strdup(ctx, "");
+
+ if (!*pp_server || !*pp_share || !*pp_path ||
+ !*pp_user || !*pp_password) {
+ return -1;
+ }
+
+ /*
+ * Assume we won't find an authentication domain to parse, so default
+ * to the workgroup in the provided context.
+ */
+ if (pp_workgroup != NULL) {
+ *pp_workgroup =
+ talloc_strdup(ctx, smbc_getWorkgroup(context));
+ }
+
+ if (pp_options) {
+ *pp_options = talloc_strdup(ctx, "");
+ }
+ s = talloc_strdup(ctx, fname);
+
+ /* see if it has the right prefix */
+ len = strlen(SMBC_PREFIX);
+ if (strncmp(s,SMBC_PREFIX,len) || (s[len] != '/' && s[len] != 0)) {
+ return -1; /* What about no smb: ? */
+ }
+
+ p = s + len;
+
+ /* Watch the test below, we are testing to see if we should exit */
+
+ if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
+ DEBUG(1, ("Invalid path (does not begin with smb://"));
+ return -1;
+ }
+
+ p += 2; /* Skip the double slash */
+
+ /* See if any options were specified */
+ if ((q = strrchr(p, '?')) != NULL ) {
+ /* There are options. Null terminate here and point to them */
+ *q++ = '\0';
+
+ DEBUG(4, ("Found options '%s'", q));
+
+ /* Copy the options */
+ if (pp_options && *pp_options != NULL) {
+ TALLOC_FREE(*pp_options);
+ *pp_options = talloc_strdup(ctx, q);
+ }
+ }
+
+ if (*p == '\0') {
+ goto decoding;
+ }
+
+ if (*p == '/') {
+ int wl = strlen(smbc_getWorkgroup(context));
+
+ if (wl > 16) {
+ wl = 16;
+ }
+
+ *pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context));
+ if (!*pp_server) {
+ return -1;
+ }
+ (*pp_server)[wl] = '\0';
+ return 0;
+ }
+
+ /*
+ * ok, its for us. Now parse out the server, share etc.
+ *
+ * However, we want to parse out [[domain;]user[:password]@] if it
+ * exists ...
+ */
+
+ /* check that '@' occurs before '/', if '/' exists at all */
+ q = strchr_m(p, '@');
+ r = strchr_m(p, '/');
+ if (q && (!r || q < r)) {
+ char *userinfo = NULL;
+ const char *u;
+
+ next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
+ if (!userinfo) {
+ return -1;
+ }
+ u = userinfo;
+
+ if (strchr_m(u, ';')) {
+ next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
+ if (!workgroup) {
+ return -1;
+ }
+ if (pp_workgroup) {
+ *pp_workgroup = workgroup;
+ }
+ }
+
+ if (strchr_m(u, ':')) {
+ next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
+ if (!*pp_user) {
+ return -1;
+ }
+ *pp_password = talloc_strdup(ctx, u);
+ if (!*pp_password) {
+ return -1;
+ }
+ } else {
+ *pp_user = talloc_strdup(ctx, u);
+ if (!*pp_user) {
+ return -1;
+ }
+ }
+ }
+
+ if (!next_token_talloc(ctx, &p, pp_server, "/")) {
+ return -1;
+ }
+
+ /*
+ * Does *pp_server contain a ':' ? If so
+ * this denotes the port.
+ */
+ q = strchr_m(*pp_server, ':');
+ if (q != NULL) {
+ long int port;
+ char *endptr = NULL;
+ *q = '\0';
+ q++;
+ if (*q == '\0') {
+ /* Bad port. */
+ return -1;
+ }
+ port = strtol(q, &endptr, 10);
+ if (*endptr != '\0') {
+ /* Bad port. */
+ return -1;
+ }
+ *p_port = (uint16_t)port;
+ }
+
+ if (*p == (char)0) {
+ goto decoding; /* That's it ... */
+ }
+
+ if (!next_token_talloc(ctx, &p, pp_share, "/")) {
+ return -1;
+ }
+
+ /*
+ * Prepend a leading slash if there's a file path, as required by
+ * NetApp filers.
+ */
+ if (*p != '\0') {
+ *pp_path = talloc_asprintf(ctx,
+ "\\%s",
+ p);
+ } else {
+ *pp_path = talloc_strdup(ctx, "");
+ }
+ if (!*pp_path) {
+ return -1;
+ }
+ string_replace(*pp_path, '/', '\\');
+
+decoding:
+ (void) urldecode_talloc(ctx, pp_path, *pp_path);
+ (void) urldecode_talloc(ctx, pp_server, *pp_server);
+ (void) urldecode_talloc(ctx, pp_share, *pp_share);
+ (void) urldecode_talloc(ctx, pp_user, *pp_user);
+ (void) urldecode_talloc(ctx, pp_password, *pp_password);
+
+ if (!workgroup) {
+ workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
+ }
+ if (!workgroup) {
+ return -1;
+ }
+
+ /* set the credentials to make DFS work */
+ smbc_set_credentials_with_fallback(context,
+ workgroup,
+ *pp_user,
+ *pp_password);
+ return 0;
+}
+