Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions storage/connect/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ SET(CONNECT_SOURCES
ha_connect.cc connect.cc user_connect.cc mycat.cc
fmdlex.c osutil.c rcmsg.c rcmsg.h
array.cpp blkfil.cpp colblk.cpp csort.cpp
filamap.cpp filamdbf.cpp filamfix.cpp filamgz.cpp filamtxt.cpp filter.cpp
filamap.cpp filamdbf.cpp filamfix.cpp filamgz.cpp filamtxt.cpp filechk.cpp filter.cpp
json.cpp jsonudf.cpp maputil.cpp myconn.cpp myutil.cpp plgdbutl.cpp plugutil.cpp
reldef.cpp tabcol.cpp tabdos.cpp tabext.cpp tabfix.cpp tabfmt.cpp tabjson.cpp
table.cpp tabmul.cpp tabmysql.cpp taboccur.cpp tabpivot.cpp tabsys.cpp tabtbl.cpp
tabutil.cpp tabvir.cpp tabxcl.cpp valblk.cpp value.cpp xindex.cpp xobject.cpp

array.h blkfil.h block.h catalog.h checklvl.h colblk.h connect.h csort.h
engmsg.h filamap.h filamdbf.h filamfix.h filamgz.h filamtxt.h
engmsg.h filamap.h filamdbf.h filamfix.h filamgz.h filamtxt.h filechk.h
filter.h global.h ha_connect.h inihandl.h json.h jsonudf.h maputil.h msgid.h
mycat.h myconn.h myutil.h os.h osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h
resource.h tabcol.h tabdos.h tabext.h tabfix.h tabfmt.h tabjson.h tabmul.h
Expand Down
26 changes: 25 additions & 1 deletion storage/connect/bsonudf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <cassert>

#include "bsonudf.h"
#include "filechk.h"

#if defined(UNIX) || defined(UNIV_LINUX)
#define _O_RDONLY O_RDONLY
Expand Down Expand Up @@ -4474,6 +4475,16 @@ char *bson_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
PlugSubSet(g->Sarea, g->Sarea_Size);
fn = MakePSZ(g, args, 0);

if (!fn) {
PUSH_WARNING("Missing file name");
*is_null = 1;
return NULL;
} else if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
*is_null = 1;
return NULL;
}
Comment on lines +4478 to +4486

if (args->arg_count > 1) {
int pretty = 3, pty = 3;
size_t len;
Expand Down Expand Up @@ -4625,7 +4636,10 @@ char *bfile_make(UDF_INIT *initid, UDF_ARGS *args, char *result,
} // endswitch arg_type

if (fn) {
if (!bnx.Serialize(g, jvp, fn, pretty))
if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
fn = NULL;
} else if (!bnx.Serialize(g, jvp, fn, pretty))
PUSH_WARNING(g->Message);
} else
PUSH_WARNING("Missing file name");
Expand Down Expand Up @@ -6061,6 +6075,16 @@ char *bbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result,

fn = MakePSZ(g, args, 0);

if (!fn) {
PUSH_WARNING("Missing file name");
*error = 1;
goto fin;
} else if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
*error = 1;
goto fin;
}

for (unsigned int i = 1; i < args->arg_count; i++)
if (args->arg_type[i] == INT_RESULT && *(longlong*)args->args[i] < 4) {
pretty = (int) * (longlong*)args->args[i];
Expand Down
41 changes: 41 additions & 0 deletions storage/connect/filechk.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* Copyright (C) MariaDB Corporation Ab

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; version 2 of the License.

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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
*/

#include <my_global.h>
#include <mysqld.h>
#include <sql_parse.h>
#include "filechk.h"

/*
@return true if the current user may access path.
*/
bool connect_can_access_file(const char *path)
{
char real_path[FN_REFLEN];

if (!path)
return false; /* callers must check for NULL before calling */

if (check_global_access(current_thd, FILE_ACL, true))
return false;

/* MY_SAFE_PATH returns NULL on overflow; fail closed rather than validate a truncated path */
if (!fn_format(real_path, path, mysql_real_data_home, "",
MY_RELATIVE_PATH | MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH | MY_SAFE_PATH))
return false;

return is_secure_file_path(real_path);
}
20 changes: 20 additions & 0 deletions storage/connect/filechk.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* Copyright (C) MariaDB Corporation Ab

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; version 2 of the License.

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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
*/

/* File access checks for CONNECT file UDFs */
#pragma once
bool connect_can_access_file(const char *path);

35 changes: 31 additions & 4 deletions storage/connect/jsonudf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <m_string.h>

#include "jsonudf.h"
#include "filechk.h"

#if defined(UNIX) || defined(UNIV_LINUX)
#define _O_RDONLY O_RDONLY
Expand Down Expand Up @@ -4555,6 +4556,16 @@ char *json_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
PlugSubSet(g->Sarea, g->Sarea_Size);
fn = MakePSZ(g, args, 0);

if (!fn) {
PUSH_WARNING("Missing file name");
*is_null = 1;
return NULL;
} else if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
*is_null = 1;
return NULL;
}

if (args->arg_count > 1) {
int pretty = 3, pty = 3;
size_t len;
Expand Down Expand Up @@ -4708,7 +4719,10 @@ char *jfile_make(UDF_INIT *initid, UDF_ARGS *args, char *result,
} // endswitch arg_type

if (fn) {
if (!Serialize(g, jvp->GetJson(), fn, pretty))
if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
fn = NULL;
} else if (!Serialize(g, jvp->GetJson(), fn, pretty))
PUSH_WARNING(g->Message);
} else
PUSH_WARNING("Missing file name");
Expand Down Expand Up @@ -5760,8 +5774,8 @@ my_bool jbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
if (args->arg_count < 1 || args->arg_count > 4) {
strcpy(message, "This function only accepts 1 to 4 arguments");
return true;
} else if (args->arg_type[0] != STRING_RESULT || !args->args[0]) {
strcpy(message, "First argument must be a constant string (file name)");
} else if (args->arg_type[0] != STRING_RESULT) {
strcpy(message, "First argument must be a string (file name)");
return true;
} // endifs

Expand All @@ -5779,7 +5793,10 @@ my_bool jbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)

initid->maybe_null = 1;
CalcLen(args, false, reslen, memlen);
fl = GetFileLength(args->args[0]);
if (args->args[0])
fl = GetFileLength(args->args[0]);
else
fl = 100; // What can be done here?
reslen += fl;
more += fl * M;
//memlen += more;
Expand All @@ -5804,6 +5821,16 @@ char *jbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
g->Xchk = NULL;
fn = MakePSZ(g, args, 0);

if (!fn) {
PUSH_WARNING("Missing file name");
*error = 1;
goto fin;
} else if (!connect_can_access_file(fn)) {
PUSH_WARNING("Access denied: FILE privilege or secure_file_priv violation");
*error = 1;
goto fin;
}

for (unsigned int i = 1; i < args->arg_count; i++)
if (args->arg_type[i] == INT_RESULT && *(longlong*)args->args[i] < 4) {
pretty = (int) * (longlong*)args->args[i];
Expand Down
156 changes: 156 additions & 0 deletions storage/connect/mysql-test/connect/r/json_bson_file_priv.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#
# Test FILE privilege and secure_file_priv enforcement for JSON/BSON file UDFs
#
# Privileged user: path inside secure_file_priv MYSQL_TMP_DIR
select json_file('MYSQL_TMP_DIR/bib0.json', 0) IS NOT NULL AS ok;
ok
1
select bson_file('MYSQL_TMP_DIR/biblio.json', 0) IS NOT NULL AS ok;
ok
1
Warnings:
Warning 1105 File pretty format doesn't match the specified pretty value
select jfile_make('{"test":1}', 'MYSQL_TMP_DIR/out.json', 0) IS NOT NULL AS ok;
ok
1
select bfile_make('{"test":1}', 'MYSQL_TMP_DIR/out.json', 0) IS NOT NULL AS ok;
ok
1
select jbin_file('MYSQL_TMP_DIR/bib0.json') IS NOT NULL AS ok;
ok
1
select bbin_file('MYSQL_TMP_DIR/biblio.json') IS NOT NULL AS ok;
ok
1
# Privileged user: path outside secure_file_priv
select json_file('DATADIR/bib0.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bson_file('DATADIR/biblio.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select jfile_make('{"test":1}', 'DATADIR/out.json', 0) IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bfile_make('{"test":1}', 'DATADIR/out.json', 0) IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select jbin_file('DATADIR/bib0.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bbin_file('DATADIR/biblio.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
# NULL argument: no "access denied", UDF reports its own error
select json_file(NULL) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
select bson_file(NULL) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
select jfile_make('{"test":1}', NULL, 0) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
select bfile_make('{"test":1}', NULL, 0) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
select jbin_file(NULL) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
select bbin_file(NULL) IS NULL AS ok;
ok
1
Warnings:
Warning 1105 Missing file name
# Privileged user: relative path resolving outside secure_file_priv
select json_file('../../bib0.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bson_file('../../biblio.json') IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
# Overlong filename: MY_SAFE_PATH rejects it, fail closed
select json_file(concat('MYSQL_TMP_DIR/', repeat('x', 512))) IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bson_file(concat('MYSQL_TMP_DIR/', repeat('x', 512))) IS NULL AS denied;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
# Non-constant filename expression (args->args[0] is NULL at init time)
set @fname = 'MYSQL_TMP_DIR/bib0.json';
select jbin_file(@fname) IS NOT NULL AS ok;
ok
1
set @fname = 'MYSQL_TMP_DIR/biblio.json';
select bbin_file(@fname) IS NOT NULL AS ok;
ok
1
# User without FILE privilege
create user unprivileged@localhost;
grant select ON *.* TO unprivileged@localhost;
connect unprivileged,localhost,unprivileged,,;
connection unprivileged;
# inside secure_file_priv: denied without FILE privilege
select json_file('MYSQL_TMP_DIR/bib0.json') IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bson_file('MYSQL_TMP_DIR/biblio.json') IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select jfile_make('{"test":1}', 'MYSQL_TMP_DIR/out.json', 0) IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bfile_make('{"test":1}', 'MYSQL_TMP_DIR/out.json', 0) IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select jbin_file('MYSQL_TMP_DIR/bib0.json') IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
select bbin_file('MYSQL_TMP_DIR/biblio.json') IS NULL AS denied;;
denied
1
Warnings:
Warning 1105 Access denied: FILE privilege or secure_file_priv violation
disconnect unprivileged;
connection default;
drop user unprivileged@localhost;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--secure_file_priv=$MYSQL_TMP_DIR
Loading