Skip to content

Commit a6c595a

Browse files
committed
Add AllowLoadLocalInfile connection string option. Fixes #643
1 parent 5159fd1 commit a6c595a

File tree

9 files changed

+81
-7
lines changed

9 files changed

+81
-7
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
lastmod: 2019-05-23
3+
date: 2019-05-23
4+
title: Load Data Local Infile
5+
weight: 30
6+
menu:
7+
main:
8+
parent: troubleshooting
9+
---
10+
11+
# Load Data Local Infile
12+
13+
## Background
14+
15+
MySQL Server supports a `LOAD DATA` command that can bulk load data from a CSV or TSV file.
16+
This normally loads data from a file on the server, but it can load from a file on the client by using
17+
the `LOAD DATA LOCAL` statement, or by setting `MySqlBulkLoader.Local = true`.
18+
19+
## Errors
20+
21+
If you do this, you may receive one of the following errors:
22+
23+
* The used command is not allowed with this MySQL version
24+
* To use MySqlBulkLoader.Local=true, set AllowLoadLocalInfile=true in the connection string.
25+
* To use LOAD DATA LOCAL INFILE, set AllowLoadLocalInfile=true in the connection string.
26+
* Use SourceStream or SslMode >= VerifyCA for LOAD DATA LOCAL INFILE.
27+
28+
## Cause
29+
30+
`LOAD DATA LOCAL INFILE` is disabled by default because it poses a security risk. A
31+
malicious server or proxy could send a fake “local infile request” packet to the client and
32+
read any file that the client has permission to open.
33+
34+
For more information, see [the MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/load-data-local.html).
35+
36+
## How to Fix
37+
38+
To allow `LOAD DATA LOCAL INFILE` to succeed, you must set `AllowLoadLocalInfile=true`
39+
in the client’s connection string.
40+
41+
If you use `MySqlBulkerLoader` and set `Local=true`, then everything should work by default.
42+
43+
If you are manually creating a `LOAD DATA LOCAL INFILE` statement, you must be connected
44+
to a trusted server. This requires specifying `SslMode=VerifyCA` or `SslMode=VerifyFull` in the
45+
connection string. Alternatively, rewrite the code to use `MySqlBulkLoader`.

src/MySqlConnector/Core/ConnectionSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
6464
MaximumPoolSize = (int) csb.MaximumPoolSize;
6565

6666
// Other Options
67+
AllowLoadLocalInfile = csb.AllowLoadLocalInfile;
6768
AllowPublicKeyRetrieval = csb.AllowPublicKeyRetrieval;
6869
AllowUserVariables = csb.AllowUserVariables;
6970
AllowZeroDateTime = csb.AllowZeroDateTime;
@@ -146,6 +147,7 @@ private static MySqlGuidFormat GetEffectiveGuidFormat(MySqlGuidFormat guidFormat
146147
public int MaximumPoolSize { get; }
147148

148149
// Other Options
150+
public bool AllowLoadLocalInfile { get; }
149151
public bool AllowPublicKeyRetrieval { get; }
150152
public bool AllowUserVariables { get; }
151153
public bool AllowZeroDateTime { get; }

src/MySqlConnector/Core/ResultSet.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,12 @@ public async Task<ResultSet> ReadResultSetHeaderAsync(IOBehavior ioBehavior)
6363
{
6464
try
6565
{
66+
if (!Connection.AllowLoadLocalInfile)
67+
throw new NotSupportedException("To use LOAD DATA LOCAL INFILE, set AllowLoadLocalInfile=true in the connection string. See https://fl.vu/mysql-load-data");
6668
var localInfile = LocalInfilePayload.Create(payload.AsSpan());
6769
if (!IsHostVerified(Connection)
6870
&& !localInfile.FileName.StartsWith(MySqlBulkLoader.StreamPrefix, StringComparison.Ordinal))
69-
throw new NotSupportedException("Use SourceStream or SslMode >= VerifyCA for LOAD DATA LOCAL INFILE");
71+
throw new NotSupportedException("Use SourceStream or SslMode >= VerifyCA for LOAD DATA LOCAL INFILE. See https://fl.vu/mysql-load-data");
7072

7173
using (var stream = localInfile.FileName.StartsWith(MySqlBulkLoader.StreamPrefix, StringComparison.Ordinal) ?
7274
MySqlBulkLoader.GetAndRemoveStream(localInfile.FileName) :

src/MySqlConnector/MySql.Data.MySqlClient/MySqlBulkLoader.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,17 +185,31 @@ private async Task<int> LoadAsync(IOBehavior ioBehavior, CancellationToken cance
185185
Connection.Open();
186186
}
187187

188+
bool closeStream = SourceStream is object;
188189
try
189190
{
191+
if (Local && !Connection.AllowLoadLocalInfile)
192+
throw new NotSupportedException("To use MySqlBulkLoader.Local=true, set AllowLoadLocalInfile=true in the connection string. See https://fl.vu/mysql-load-data");
193+
190194
var commandString = BuildSqlCommand();
191195
var cmd = new MySqlCommand(commandString, Connection, Connection.CurrentTransaction)
192196
{
193197
CommandTimeout = Timeout,
194198
};
195-
return await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
199+
var result = await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
200+
closeStream = false;
201+
return result;
196202
}
197203
finally
198204
{
205+
if (closeStream)
206+
{
207+
using (GetAndRemoveStream(FileName))
208+
{
209+
// close the stream
210+
}
211+
}
212+
199213
if (closeConnection)
200214
Connection.Close();
201215
}

src/MySqlConnector/MySql.Data.MySqlClient/MySqlConnection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ internal async Task<CachedProcedure> GetCachedProcedure(IOBehavior ioBehavior, s
514514
}
515515

516516
internal MySqlTransaction CurrentTransaction { get; set; }
517+
internal bool AllowLoadLocalInfile => m_connectionSettings.AllowLoadLocalInfile;
517518
internal bool AllowUserVariables => m_connectionSettings.AllowUserVariables;
518519
internal bool AllowZeroDateTime => m_connectionSettings.AllowZeroDateTime;
519520
internal bool ConvertZeroDateTime => m_connectionSettings.ConvertZeroDateTime;

src/MySqlConnector/MySql.Data.MySqlClient/MySqlConnectionStringBuilder.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ public uint MaximumPoolSize
166166
}
167167

168168
// Other Options
169+
public bool AllowLoadLocalInfile
170+
{
171+
get => MySqlConnectionStringOption.AllowLoadLocalInfile.GetValue(this);
172+
set => MySqlConnectionStringOption.AllowLoadLocalInfile.SetValue(this, value);
173+
}
174+
169175
public bool AllowPublicKeyRetrieval
170176
{
171177
get => MySqlConnectionStringOption.AllowPublicKeyRetrieval.GetValue(this);
@@ -385,6 +391,7 @@ internal abstract class MySqlConnectionStringOption
385391
public static readonly MySqlConnectionStringOption<uint> MaximumPoolSize;
386392

387393
// Other Options
394+
public static readonly MySqlConnectionStringOption<bool> AllowLoadLocalInfile;
388395
public static readonly MySqlConnectionStringOption<bool> AllowPublicKeyRetrieval;
389396
public static readonly MySqlConnectionStringOption<bool> AllowUserVariables;
390397
public static readonly MySqlConnectionStringOption<bool> AllowZeroDateTime;
@@ -532,6 +539,10 @@ static MySqlConnectionStringOption()
532539
defaultValue: 100));
533540

534541
// Other Options
542+
AddOption(AllowLoadLocalInfile = new MySqlConnectionStringOption<bool>(
543+
keys: new[] { "AllowLoadLocalInfile", "Allow Load Local Infile" },
544+
defaultValue: false));
545+
535546
AddOption(AllowPublicKeyRetrieval = new MySqlConnectionStringOption<bool>(
536547
keys: new[] { "AllowPublicKeyRetrieval", "Allow Public Key Retrieval" },
537548
defaultValue: false));

src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
2020
(serverCapabilities & ProtocolCapabilities.PluginAuthLengthEncodedClientData) |
2121
ProtocolCapabilities.MultiStatements |
2222
ProtocolCapabilities.MultiResults |
23-
ProtocolCapabilities.LocalFiles |
23+
(cs.AllowLoadLocalInfile ? (serverCapabilities & ProtocolCapabilities.LocalFiles) : 0) |
2424
(string.IsNullOrWhiteSpace(cs.Database) ? 0 : ProtocolCapabilities.ConnectWithDatabase) |
2525
(cs.UseAffectedRows ? 0 : ProtocolCapabilities.FoundRows) |
2626
(useCompression ? ProtocolCapabilities.Compress : ProtocolCapabilities.None) |

tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public class MySqlConnectionStringBuilderTests
1010
public void Defaults()
1111
{
1212
var csb = new MySqlConnectionStringBuilder();
13+
Assert.False(csb.AllowLoadLocalInfile);
1314
Assert.False(csb.AllowPublicKeyRetrieval);
1415
Assert.False(csb.AllowUserVariables);
1516
Assert.False(csb.AllowZeroDateTime);
@@ -78,6 +79,7 @@ public void ParseConnectionString()
7879
{
7980
ConnectionString = "Data Source=db-server;" +
8081
"Initial Catalog=schema_name;" +
82+
"allow load local infile=true;" +
8183
"allowpublickeyretrieval = true;" +
8284
"Allow User Variables=true;" +
8385
"allow zero datetime=true;" +
@@ -128,6 +130,7 @@ public void ParseConnectionString()
128130
"Uid=username;" +
129131
"useaffectedrows=true"
130132
};
133+
Assert.True(csb.AllowLoadLocalInfile);
131134
Assert.True(csb.AllowPublicKeyRetrieval);
132135
Assert.True(csb.AllowUserVariables);
133136
Assert.True(csb.AllowZeroDateTime);

tests/SideBySide/BulkLoaderSync.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -542,13 +542,9 @@ public void BulkLoadLocalMemoryStream()
542542

543543
internal static string GetLocalConnectionString()
544544
{
545-
#if BASELINE
546545
var csb = AppConfig.CreateConnectionStringBuilder();
547546
csb.AllowLoadLocalInfile = true;
548547
return csb.ConnectionString;
549-
#else
550-
return AppConfig.ConnectionString;
551-
#endif
552548
}
553549

554550
readonly string m_testTable;

0 commit comments

Comments
 (0)