Skip to content

Commit a625b39

Browse files
committed
CDRIVER-2257 security rules for mongodb+srv://
Hostnames returned from an SRV lookup must share the service name's parent domain, and SSL is turned on with mongodb+srv URIs by default. At most one TXT record is allowed, only "replicaSet" and "authSource" may be set from a TXT record.
1 parent 50c1831 commit a625b39

29 files changed

+654
-136
lines changed

.evergreen/config.yml

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ functions:
296296
working_dir: "mongoc"
297297
script: |
298298
set -o errexit
299-
COMPRESSORS="${COMPRESSORS}" CC="${CC}" AUTH=${AUTH} SSL=${SSL} URI=${URI} IPV4_ONLY=${IPV4_ONLY} VALGRIND=${VALGRIND} MONGOC_TEST_URI=${URI} sh .evergreen/run-tests.sh
299+
COMPRESSORS="${COMPRESSORS}" CC="${CC}" AUTH=${AUTH} SSL=${SSL} URI=${URI} IPV4_ONLY=${IPV4_ONLY} VALGRIND=${VALGRIND} MONGOC_TEST_URI=${URI} DNS=${DNS} sh .evergreen/run-tests.sh
300300
301301
"run auth tests":
302302
- command: shell.exec
@@ -912,21 +912,56 @@ tasks:
912912
vars:
913913
ENABLE_SSL: "1"
914914

915-
- name: test-dns
915+
- name: test-dns-openssl
916916
depends_on:
917-
- name: "debug-compile-nosasl-nossl"
917+
- name: "debug-compile-sasl-openssl"
918918
commands:
919919
- func: "fetch build"
920920
vars:
921-
BUILD_NAME: "debug-compile-nosasl-nossl"
922-
# The Initial DNS Seedlist Discovery Spec's tests use 3-node replica set
921+
BUILD_NAME: "debug-compile-sasl-openssl"
923922
- func: "bootstrap mongo-orchestration"
924923
vars:
925-
VERSION: "3.4"
924+
VERSION: "latest"
925+
TOPOLOGY: "replica_set"
926+
SSL: "ssl"
927+
- func: "run tests"
928+
vars:
929+
DNS: on
930+
SSL: "ssl"
931+
932+
- name: test-dns-winssl
933+
depends_on:
934+
- name: "debug-compile-sspi-winssl"
935+
commands:
936+
- func: "fetch build"
937+
vars:
938+
BUILD_NAME: "debug-compile-sspi-winssl"
939+
- func: "bootstrap mongo-orchestration"
940+
vars:
941+
VERSION: "latest"
926942
TOPOLOGY: "replica_set"
943+
SSL: "winssl"
927944
- func: "run tests"
928945
vars:
929946
DNS: on
947+
SSL: "winssl"
948+
949+
- name: test-dns-darwinssl
950+
depends_on:
951+
- name: "debug-compile-sasl-darwinssl"
952+
commands:
953+
- func: "fetch build"
954+
vars:
955+
BUILD_NAME: "debug-compile-sasl-darwinssl"
956+
- func: "bootstrap mongo-orchestration"
957+
vars:
958+
VERSION: "latest"
959+
TOPOLOGY: "replica_set"
960+
SSL: "darwinssl"
961+
- func: "run tests"
962+
vars:
963+
DNS: on
964+
SSL: "darwinssl"
930965

931966
- name: debug-compile-nosasl-nossl
932967
tags: ["debug-compile", "nosasl", "nossl"]
@@ -7271,12 +7306,6 @@ buildvariants:
72717306
- name: "link-with-pkg-config-mac"
72727307
distros:
72737308
- macos-1012
7274-
- name: "test-dns"
7275-
distros:
7276-
- ubuntu1604-build
7277-
- ubuntu1604-test
7278-
- ubuntu1404-build
7279-
- ubuntu1404-test
72807309

72817310

72827311
- name: clang34ubuntu
@@ -7532,6 +7561,7 @@ buildvariants:
75327561
- "retry-true-latest-server"
75337562
# Ubuntu16.04 only supports MongoDB 3.4+
75347563
- ".3.6 .openssl !.nosasl .server"
7564+
- "test-dns-openssl"
75357565

75367566
- name: darwin
75377567
display_name: "*Darwin, macOS (Apple LLVM)"
@@ -7556,7 +7586,7 @@ buildvariants:
75567586
- ".3.2 .darwinssl !.nosasl .server"
75577587
- ".3.2 .nossl !.nosasl"
75587588
- ".2.6 .nossl !.nosasl" # No MongoDB SSL builds available
7559-
- "test-dns"
7589+
- "test-dns-darwinssl"
75607590

75617591
- name: windows-2015
75627592
display_name: "Windows (VS 2015)"
@@ -7584,7 +7614,7 @@ buildvariants:
75847614
- ".3.2 .winssl !.nosasl .server"
75857615
- ".3.0 .nossl !.nosasl"
75867616
- ".2.6 .nossl !.nosasl"
7587-
- "test-dns"
7617+
- "test-dns-winssl"
75887618

75897619
- name: windows-2015-32
75907620
display_name: "Windows (i386) (VS 2015)"
@@ -7696,7 +7726,6 @@ buildvariants:
76967726
- ".latest .nossl .nosasl"
76977727
- ".latest .sspi"
76987728
- ".3.6 .winssl .nosasl .server"
7699-
- "test-dns"
77007729

77017730
- name: mingw
77027731
display_name: "MinGW-W64"
@@ -7752,7 +7781,7 @@ buildvariants:
77527781
- ".latest .nossl !.nosasl"
77537782
- ".3.6 .openssl !.nosasl .server"
77547783
# Ubuntu16.04 ppc64le is only supported by MongoDB 3.4+
7755-
- "test-dns"
7784+
- "test-dns-openssl"
77567785

77577786
- name: arm-ubuntu1604
77587787
display_name: "*ARM (aarch64) (Ubuntu 16.04)"
@@ -7776,7 +7805,7 @@ buildvariants:
77767805
- ".latest .nossl !.nosasl"
77777806
- ".3.6 .openssl !.nosasl .server"
77787807
# Ubuntu16.04 aarch64 is only supported by MongoDB 3.4+
7779-
- "test-dns"
7808+
- "test-dns-openssl"
77807809

77817810
- name: zseries-rhel72
77827811
display_name: "*zSeries (s390x) (RHEL 7.2)"
@@ -7818,7 +7847,7 @@ buildvariants:
78187847
- ".latest .nossl !.nosasl"
78197848
- ".3.6 .openssl !.nosasl .server"
78207849
# Ubuntu16.04 s390x is only supported by MongoDB 3.4+
7821-
- "test-dns"
7850+
- "test-dns-openssl"
78227851

78237852
- name: zseries-suse12
78247853
display_name: "zSeries (s390x) SUSE12"

.evergreen/run-tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ fi
4646

4747
if [ "$DNS" != "nodns" ]; then
4848
export MONGOC_TEST_DNS=on
49-
TEST_ARGS="$TEST_ARGS -l '/initial_dns_seedlist_discovery*'"
49+
TEST_ARGS="$TEST_ARGS -l /initial_dns_seedlist_discovery*"
5050
fi
5151

5252
if [ "$CC" = "mingw" ]; then

doc/mongoc_uri_t.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ You would use a connection string that resembles the following.
7070
SRV Example
7171
-----------
7272

73-
If you have configured an `SRV record <https://www.ietf.org/rfc/rfc2782.txt>`_ with a name like "_mongodb._tcp.example.com" whose records are a list of one or more MongoDB server hostnames, use a connection string like this:
73+
If you have configured an `SRV record <https://www.ietf.org/rfc/rfc2782.txt>`_ with a name like "_mongodb._tcp.server.example.com" whose records are a list of one or more MongoDB server hostnames, use a connection string like this:
7474

7575
.. code-block:: c
7676
77-
uri = mongoc_uri_new ("mongodb+srv://example.com/?replicaSet=rs&appName=applicationName");
77+
uri = mongoc_uri_new ("mongodb+srv://server.example.com/?replicaSet=rs&appName=applicationName");
7878
7979
The driver prefixes the service name with "_mongodb._tcp.", then performs a DNS SRV query to resolve the service name to one or more hostnames. If this query succeeds, the driver performs a DNS TXT query on the service name (without the "_mongodb._tcp" prefix) for additional URI options configured as TXT records.
8080

src/mongoc/mongoc-client.c

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,8 @@ srv_callback (const char *service,
109109
mongoc_uri_t *uri,
110110
bson_error_t *error)
111111
{
112-
mongoc_uri_append_host (
113-
uri, pdns->Data.SRV.pNameTarget, pdns->Data.SRV.wPort);
114-
115-
return true;
112+
return mongoc_uri_append_host (
113+
uri, pdns->Data.SRV.pNameTarget, pdns->Data.SRV.wPort, error);
116114
}
117115

118116
static bool
@@ -122,20 +120,19 @@ txt_callback (const char *service,
122120
bson_error_t *error)
123121
{
124122
DWORD i;
123+
bson_string_t *txt;
124+
bool r;
125+
126+
txt = bson_string_new (NULL);
125127

126128
for (i = 0; i < pdns->Data.TXT.dwStringCount; i++) {
127-
/* Initial DNS Seedlist Discovery Spec: "Client MUST use options specified
128-
* in the Connection String to override options provided through TXT
129-
* records." So, do NOT override existing options with TXT options. */
130-
if (!mongoc_uri_parse_options (uri,
131-
pdns->Data.TXT.pStringArray[i],
132-
false /* override */,
133-
error)) {
134-
return false;
135-
}
129+
bson_string_append (txt, pdns->Data.TXT.pStringArray[i]);
136130
}
137131

138-
return true;
132+
r = mongoc_uri_parse_options (uri, txt->str, true /* from_dns */, error);
133+
bson_string_free (txt, true);
134+
135+
return r;
139136
}
140137

141138
/*
@@ -149,6 +146,12 @@ txt_callback (const char *service,
149146
* Returns:
150147
* Success or failure.
151148
*
149+
* For an SRV lookup, returns false if there is any error.
150+
*
151+
* For TXT lookup, ignores any error fetching the resource record, but
152+
* returns false if the resource record is found and there is an error
153+
* reading its contents as URI options.
154+
*
152155
* Side effects:
153156
* @error is set if there is a failure.
154157
*
@@ -167,15 +170,21 @@ _mongoc_get_rr_dnsapi (const char *service,
167170
PDNS_RECORD pdns = NULL;
168171
DNS_STATUS res;
169172
LPVOID lpMsgBuf = NULL;
170-
bool ret = false;
173+
bool dns_success;
174+
bool callback_success = true;
175+
int i;
171176

172177
ENTRY;
173178

174179
if (rr_type == MONGOC_RR_SRV) {
180+
/* return true only if DNS succeeds */
181+
dns_success = false;
175182
rr_type_name = "SRV";
176183
nst = DNS_TYPE_SRV;
177184
callback = srv_callback;
178185
} else {
186+
/* return true whether or not DNS succeeds */
187+
dns_success = true;
179188
rr_type_name = "TXT";
180189
nst = DNS_TYPE_TEXT;
181190
callback = txt_callback;
@@ -214,14 +223,25 @@ _mongoc_get_rr_dnsapi (const char *service,
214223
DNS_ERROR ("No %s records for \"%s\"", rr_type_name, service);
215224
}
216225

226+
dns_success = true;
227+
i = 0;
228+
217229
do {
230+
if (i > 0 && rr_type == MONGOC_RR_TXT) {
231+
/* Initial DNS Seedlist Discovery Spec: a client "MUST raise an error
232+
* when multiple TXT records are encountered". */
233+
callback_success = false;
234+
DNS_ERROR ("Multiple TXT records for \"%s\"", service);
235+
}
236+
218237
if (!callback (service, pdns, uri, error)) {
238+
callback_success = false;
219239
GOTO (done);
220240
}
221241
pdns = pdns->pNext;
242+
i++;
222243
} while (pdns);
223244

224-
ret = true;
225245
done:
226246
if (pdns) {
227247
DnsRecordListFree (pdns, DnsFreeRecordList);
@@ -231,7 +251,7 @@ _mongoc_get_rr_dnsapi (const char *service,
231251
LocalFree (lpMsgBuf);
232252
}
233253

234-
RETURN (ret);
254+
RETURN (dns_success && callback_success);
235255
}
236256

237257
#elif (defined(MONGOC_HAVE_RES_NSEARCH) || defined(MONGOC_HAVE_RES_SEARCH))
@@ -269,8 +289,7 @@ srv_callback (const char *service,
269289
strerror (h_errno));
270290
}
271291

272-
mongoc_uri_append_host (uri, name, port);
273-
ret = true;
292+
ret = mongoc_uri_append_host (uri, name, port, error);
274293

275294
done:
276295
return ret;
@@ -309,10 +328,7 @@ txt_callback (const char *service,
309328
pos += len;
310329
}
311330

312-
/* Initial DNS Seedlist Discovery Spec: "Client MUST use options specified in
313-
* the Connection String to override options provided through TXT records."
314-
* So, do NOT override existing options with TXT options. */
315-
r = mongoc_uri_parse_options (uri, txt->str, false /* override */, error);
331+
r = mongoc_uri_parse_options (uri, txt->str, true /* from_dns */, error);
316332
bson_string_free (txt, true);
317333

318334
done:
@@ -329,6 +345,12 @@ txt_callback (const char *service,
329345
* Returns:
330346
* Success or failure.
331347
*
348+
* For an SRV lookup, returns false if there is any error.
349+
*
350+
* For TXT lookup, ignores any error fetching the resource record, but
351+
* returns false if the resource record is found and there is an error
352+
* reading its contents as URI options.
353+
*
332354
* Side effects:
333355
* @error is set if there is a failure.
334356
*
@@ -353,15 +375,20 @@ _mongoc_get_rr_search (const char *service,
353375
ns_type nst;
354376
mongoc_rr_callback_t callback;
355377
ns_rr resource_record;
356-
bool ret = false;
378+
bool dns_success;
379+
bool callback_success = true;
357380

358381
ENTRY;
359382

360383
if (rr_type == MONGOC_RR_SRV) {
384+
/* return true only if DNS succeeds */
385+
dns_success = false;
361386
rr_type_name = "SRV";
362387
nst = ns_t_srv;
363388
callback = srv_callback;
364389
} else {
390+
/* return true whether or not DNS succeeds */
391+
dns_success = true;
365392
rr_type_name = "TXT";
366393
nst = ns_t_txt;
367394
callback = txt_callback;
@@ -393,6 +420,13 @@ _mongoc_get_rr_search (const char *service,
393420
}
394421

395422
for (i = 0; i < n; i++) {
423+
if (i > 0 && rr_type == MONGOC_RR_TXT) {
424+
/* Initial DNS Seedlist Discovery Spec: a client "MUST raise an error
425+
* when multiple TXT records are encountered". */
426+
callback_success = false;
427+
DNS_ERROR ("Multiple TXT records for \"%s\"", service);
428+
}
429+
396430
if (ns_parserr (&ns_answer, ns_s_an, i, &resource_record)) {
397431
DNS_ERROR ("Invalid record %d of %s answer for \"%s\": \"%s\"",
398432
i,
@@ -402,11 +436,12 @@ _mongoc_get_rr_search (const char *service,
402436
}
403437

404438
if (!callback (service, &ns_answer, &resource_record, uri, error)) {
439+
callback_success = false;
405440
GOTO (done);
406441
}
407442
}
408443

409-
ret = true;
444+
dns_success = true;
410445

411446
done:
412447

@@ -417,7 +452,7 @@ _mongoc_get_rr_search (const char *service,
417452
/* defined on Linux, and only if MONGOC_HAVE_RES_NSEARCH is defined */
418453
res_nclose (&state);
419454
#endif
420-
RETURN (ret);
455+
RETURN (dns_success && callback_success);
421456
}
422457
#endif
423458

@@ -930,7 +965,11 @@ mongoc_client_new_from_uri (const mongoc_uri_t *uri)
930965

931966
topology = mongoc_topology_new (uri, true);
932967

933-
return _mongoc_client_new_from_uri (uri, topology);
968+
/* topology->uri may be different from uri: if this is a mongodb+srv:// URI
969+
* then mongoc_topology_new has fetched SRV and TXT records and updated its
970+
* uri from them.
971+
*/
972+
return _mongoc_client_new_from_uri (topology->uri, topology);
934973
}
935974

936975
/*

0 commit comments

Comments
 (0)