/src/dovecot/src/lib-sasl/sasl-oauth2.c
Line | Count | Source |
1 | | /* Copyright (c) 2025 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | |
5 | | #include "sasl-oauth2.h" |
6 | | |
7 | | static const unsigned char key_mask = BIT(0); |
8 | | static const unsigned char value_mask = BIT(1); |
9 | | |
10 | | static const unsigned char char_lookup[256] = { |
11 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 0, 0, // 00 |
12 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 |
13 | | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 20 |
14 | | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 30 |
15 | | 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 40 |
16 | | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, // 50 |
17 | | 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 60 |
18 | | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 0, // 70 |
19 | | |
20 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 |
21 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90 |
22 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0 |
23 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // b0 |
24 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0 |
25 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // d0 |
26 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0 |
27 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // f0 |
28 | | }; |
29 | | |
30 | | |
31 | | int sasl_oauth2_kvpair_parse(const unsigned char *data, size_t size, |
32 | | const char **key_r, const char **value_r, |
33 | | const unsigned char **end_r, |
34 | | const char **error_r) |
35 | 427 | { |
36 | 427 | const unsigned char *p = data, *pend = data + size, *poffset; |
37 | | |
38 | 427 | i_assert(p < pend); |
39 | | |
40 | | /* RFC 7628, Section 3.1: |
41 | | |
42 | | kvsep = %x01 |
43 | | key = 1*(ALPHA) |
44 | | value = *(VCHAR / SP / HTAB / CR / LF ) |
45 | | kvpair = key "=" value kvsep |
46 | | */ |
47 | | |
48 | | /* key = 1*(ALPHA) */ |
49 | 427 | poffset = p; |
50 | 2.37k | while (p < pend && (char_lookup[*p] & key_mask) != 0x00) |
51 | 1.94k | p++; |
52 | | |
53 | | /* "=" */ |
54 | 427 | if (p == pend) { |
55 | 3 | *error_r = "Missing value"; |
56 | 3 | return -1; |
57 | 3 | } |
58 | 424 | if (*p != '=') { |
59 | 19 | *error_r = "Invalid character in key"; |
60 | 19 | return -1; |
61 | 19 | } |
62 | 405 | if (p == poffset) { |
63 | 1 | *error_r = "Empty key name"; |
64 | 1 | return -1; |
65 | 1 | } |
66 | 404 | *key_r = t_strdup_until(poffset, p); |
67 | 404 | p++; |
68 | | |
69 | | /* value = *(VCHAR / SP / HTAB / CR / LF ) */ |
70 | 404 | poffset = p; |
71 | 160k | while (p < pend && (char_lookup[*p] & value_mask) != 0x00) |
72 | 160k | p++; |
73 | | |
74 | 404 | if (p == pend) { |
75 | 2 | *error_r = "Missing separator (0x01)"; |
76 | 2 | return -1; |
77 | 2 | } |
78 | 402 | if (*p != 0x01) { |
79 | 5 | *error_r = "Invalid character in value"; |
80 | 5 | return -1; |
81 | 5 | } |
82 | 397 | *value_r = t_strdup_until(poffset, p); |
83 | 397 | p++; |
84 | | |
85 | 397 | *end_r = p; |
86 | 397 | return 0; |
87 | 402 | } |
88 | | |
89 | | bool sasl_oauth2_kvpair_check_value(const char *value) |
90 | 946 | { |
91 | 946 | const unsigned char *p = (const unsigned char *)value; |
92 | 946 | const unsigned char *pend = p + strlen(value); |
93 | | |
94 | 166k | while (p < pend && (char_lookup[*p] & value_mask) != 0x00) |
95 | 165k | p++; |
96 | | |
97 | 946 | return (p == pend); |
98 | 946 | } |