Skip to content

Commit 0efbe67

Browse files
committed
fix: validate Group IDs and SecureJoin tokens
1 parent 73016a4 commit 0efbe67

File tree

6 files changed

+72
-12
lines changed

6 files changed

+72
-12
lines changed

spec.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,9 @@ All group members form the member list.
119119
To allow different groups with the same members,
120120
groups are identified by a group-id.
121121
The group-id MUST be created only from the characters
122-
`0`-`9`, `A`-`Z`, `a`-`z` `_` and `-`
123-
and MUST have a length of at least 11 characters.
122+
`0`-`9`, `A`-`Z`, `a`-`z` `_` and `-`,
123+
MUST have a length of at least 11 characters
124+
and no more than 32 characters.
124125

125126
Groups MUST have a group-name.
126127
The group-name is any non-zero-length UTF-8 string.

src/qr.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::message::Message;
2323
use crate::peerstate::Peerstate;
2424
use crate::socks::Socks5Config;
2525
use crate::token;
26+
use crate::tools::validate_id;
2627

2728
const OPENPGP4FPR_SCHEME: &str = "OPENPGP4FPR:"; // yes: uppercase
2829
const IDELTACHAT_SCHEME: &str = "https://i.delta.chat/#";
@@ -345,9 +346,18 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Result<Qr> {
345346
"".to_string()
346347
};
347348

348-
let invitenumber = param.get("i").map(|s| s.to_string());
349-
let authcode = param.get("s").map(|s| s.to_string());
350-
let grpid = param.get("x").map(|s| s.to_string());
349+
let invitenumber = param
350+
.get("i")
351+
.filter(|&s| validate_id(s))
352+
.map(|s| s.to_string());
353+
let authcode = param
354+
.get("s")
355+
.filter(|&s| validate_id(s))
356+
.map(|s| s.to_string());
357+
let grpid = param
358+
.get("x")
359+
.filter(|&s| validate_id(s))
360+
.map(|s| s.to_string());
351361

352362
let grpname = if grpid.is_some() {
353363
if let Some(encoded_name) = param.get("g") {
@@ -1035,6 +1045,21 @@ mod tests {
10351045
Ok(())
10361046
}
10371047

1048+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1049+
async fn test_decode_openpgp_invalid_token() -> Result<()> {
1050+
let ctx = TestContext::new().await;
1051+
1052+
// Token cannot contain "/"
1053+
let qr = check_qr(
1054+
&ctx.ctx,
1055+
"OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&g=test%20%3F+test%20%21&x=h-0oKQf2CDK&i=9JEXlxAqGM0&s=0V7LzL/cxRL"
1056+
).await?;
1057+
1058+
assert!(matches!(qr, Qr::FprMismatch { .. }));
1059+
1060+
Ok(())
1061+
}
1062+
10381063
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
10391064
async fn test_decode_openpgp_secure_join() -> Result<()> {
10401065
let ctx = TestContext::new().await;

src/receive_imf.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ use crate::simplify;
3838
use crate::sql;
3939
use crate::stock_str;
4040
use crate::sync::Sync::*;
41-
use crate::tools::{self, buf_compress, extract_grpid_from_rfc724_mid, strip_rtlo_characters};
41+
use crate::tools::{
42+
self, buf_compress, extract_grpid_from_rfc724_mid, strip_rtlo_characters, validate_id,
43+
};
4244
use crate::{contact, imap};
4345

4446
/// This is the struct that is returned after receiving one email (aka MIME message).
@@ -2356,7 +2358,10 @@ async fn apply_mailinglist_changes(
23562358
}
23572359

23582360
fn try_getting_grpid(mime_parser: &MimeMessage) -> Option<String> {
2359-
if let Some(optional_field) = mime_parser.get_header(HeaderDef::ChatGroupId) {
2361+
if let Some(optional_field) = mime_parser
2362+
.get_header(HeaderDef::ChatGroupId)
2363+
.filter(|s| validate_id(s))
2364+
{
23602365
return Some(optional_field.clone());
23612366
}
23622367

src/receive_imf/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,7 +1693,7 @@ async fn test_in_reply_to_two_member_group() {
16931693
Subject: foo\n\
16941694
Message-ID: <message@example.org>\n\
16951695
Chat-Version: 1.0\n\
1696-
Chat-Group-ID: foo\n\
1696+
Chat-Group-ID: foobarbaz12\n\
16971697
Chat-Group-Name: foo\n\
16981698
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
16991699
\n\
@@ -1738,7 +1738,7 @@ async fn test_in_reply_to_two_member_group() {
17381738
Message-ID: <chatreply@example.org>\n\
17391739
In-Reply-To: <message@example.org>\n\
17401740
Chat-Version: 1.0\n\
1741-
Chat-Group-ID: foo\n\
1741+
Chat-Group-ID: foobarbaz12\n\
17421742
Date: Sun, 22 Mar 2020 22:37:57 +0000\n\
17431743
\n\
17441744
chat reply\n",

src/tools.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,14 @@ pub(crate) fn create_id() -> String {
277277
.collect()
278278
}
279279

280+
/// Returns true if given string is a valid ID.
281+
///
282+
/// All IDs generated with `create_id()` should be considered valid.
283+
pub(crate) fn validate_id(s: &str) -> bool {
284+
let alphabet = base64::alphabet::URL_SAFE.as_str();
285+
s.chars().all(|c| alphabet.contains(c)) && s.len() > 10 && s.len() <= 32
286+
}
287+
280288
/// Function generates a Message-ID that can be used for a new outgoing message.
281289
/// - this function is called for all outgoing messages.
282290
/// - the message ID should be globally unique
@@ -966,6 +974,27 @@ DKIM Results: Passed=true, Works=true, Allow_Keychange=true";
966974
assert_eq!(buf.len(), 11);
967975
}
968976

977+
#[test]
978+
fn test_validate_id() {
979+
for _ in 0..10 {
980+
assert!(validate_id(&create_id()));
981+
}
982+
983+
assert_eq!(validate_id("aaaaaaaaaaaa"), true);
984+
assert_eq!(validate_id("aa-aa_aaaXaa"), true);
985+
986+
// ID cannot contain whitespace.
987+
assert_eq!(validate_id("aaaaa aaaaaa"), false);
988+
assert_eq!(validate_id("aaaaa\naaaaaa"), false);
989+
990+
// ID cannot contain "/", "+".
991+
assert_eq!(validate_id("aaaaa/aaaaaa"), false);
992+
assert_eq!(validate_id("aaaaaaaa+aaa"), false);
993+
994+
// Too long ID.
995+
assert_eq!(validate_id("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), false);
996+
}
997+
969998
#[test]
970999
fn test_create_id_invalid_chars() {
9711000
for _ in 1..1000 {

src/update_helper.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ mod tests {
164164
To: alice@example.org\n\
165165
Message-ID: <msg1@example.org>\n\
166166
Chat-Version: 1.0\n\
167-
Chat-Group-ID: abcde\n\
167+
Chat-Group-ID: abcde123456\n\
168168
Chat-Group-Name: initial name\n\
169169
Date: Sun, 22 Mar 2021 01:00:00 +0000\n\
170170
\n\
@@ -182,7 +182,7 @@ mod tests {
182182
To: alice@example.org\n\
183183
Message-ID: <msg3@example.org>\n\
184184
Chat-Version: 1.0\n\
185-
Chat-Group-ID: abcde\n\
185+
Chat-Group-ID: abcde123456\n\
186186
Chat-Group-Name: another name update\n\
187187
Chat-Group-Name-Changed: a name update\n\
188188
Date: Sun, 22 Mar 2021 03:00:00 +0000\n\
@@ -197,7 +197,7 @@ mod tests {
197197
To: alice@example.org\n\
198198
Message-ID: <msg2@example.org>\n\
199199
Chat-Version: 1.0\n\
200-
Chat-Group-ID: abcde\n\
200+
Chat-Group-ID: abcde123456\n\
201201
Chat-Group-Name: a name update\n\
202202
Chat-Group-Name-Changed: initial name\n\
203203
Date: Sun, 22 Mar 2021 02:00:00 +0000\n\

0 commit comments

Comments
 (0)