Skip to content

Commit 0a97357

Browse files
authored
Merge pull request #19814 from bdrodes/codescanning_fixes_cpp
Crypto: Fix QL-for-QL alerts and refactor type standardization
2 parents 5a176d6 + 652e7ba commit 0a97357

20 files changed

+746
-679
lines changed

cpp/ql/lib/experimental/quantum/Language.qll

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ module ArtifactFlowConfig implements DataFlow::ConfigSig {
5656
module ArtifactFlow = DataFlow::Global<ArtifactFlowConfig>;
5757

5858
/**
59-
* Artifact output to node input configuration
59+
* An artifact output to node input configuration
6060
*/
6161
abstract class AdditionalFlowInputStep extends DataFlow::Node {
6262
abstract DataFlow::Node getOutput();
@@ -91,9 +91,8 @@ module GenericDataSourceFlowConfig implements DataFlow::ConfigSig {
9191

9292
module GenericDataSourceFlow = TaintTracking::Global<GenericDataSourceFlowConfig>;
9393

94-
private class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceof Literal {
95-
ConstantDataSource() { this instanceof OpenSslGenericSourceCandidateLiteral }
96-
94+
private class ConstantDataSource extends Crypto::GenericConstantSourceInstance instanceof OpenSslGenericSourceCandidateLiteral
95+
{
9796
override DataFlow::Node getOutputNode() { result.asExpr() = this }
9897

9998
override predicate flowsTo(Crypto::FlowAwareElement other) {

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/AlgToAVCFlow.qll

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ module KnownOpenSslAlgorithmToAlgorithmValueConsumerConfig implements DataFlow::
4848
module KnownOpenSslAlgorithmToAlgorithmValueConsumerFlow =
4949
DataFlow::Global<KnownOpenSslAlgorithmToAlgorithmValueConsumerConfig>;
5050

51-
module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig implements DataFlow::ConfigSig {
51+
module RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig implements DataFlow::ConfigSig {
5252
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof OpenSslPaddingLiteral }
5353

5454
predicate isSink(DataFlow::Node sink) {
@@ -60,8 +60,8 @@ module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig implements DataF
6060
}
6161
}
6262

63-
module RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow =
64-
DataFlow::Global<RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig>;
63+
module RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow =
64+
DataFlow::Global<RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerConfig>;
6565

6666
class OpenSslAlgorithmAdditionalFlowStep extends AdditionalFlowInputStep {
6767
OpenSslAlgorithmAdditionalFlowStep() { exists(AlgorithmPassthroughCall c | c.getInNode() = this) }
@@ -114,11 +114,11 @@ class CopyAndDupAlgorithmPassthroughCall extends AlgorithmPassthroughCall {
114114
override DataFlow::Node getOutNode() { result = outNode }
115115
}
116116

117-
class NIDToPointerPassthroughCall extends AlgorithmPassthroughCall {
117+
class NidToPointerPassthroughCall extends AlgorithmPassthroughCall {
118118
DataFlow::Node inNode;
119119
DataFlow::Node outNode;
120120

121-
NIDToPointerPassthroughCall() {
121+
NidToPointerPassthroughCall() {
122122
this.getTarget().getName() in ["OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn"] and
123123
inNode.asExpr() = this.getArgument(0) and
124124
outNode.asExpr() = this
@@ -150,11 +150,11 @@ class PointerToPointerPassthroughCall extends AlgorithmPassthroughCall {
150150
override DataFlow::Node getOutNode() { result = outNode }
151151
}
152152

153-
class PointerToNIDPassthroughCall extends AlgorithmPassthroughCall {
153+
class PointerToNidPassthroughCall extends AlgorithmPassthroughCall {
154154
DataFlow::Node inNode;
155155
DataFlow::Node outNode;
156156

157-
PointerToNIDPassthroughCall() {
157+
PointerToNidPassthroughCall() {
158158
this.getTarget().getName() in ["OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid", "OBJ_txt2nid"] and
159159
(
160160
inNode.asIndirectExpr() = this.getArgument(0)

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/BlockAlgorithmInstance.qll

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,35 @@ private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmCon
55
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer
66
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase
77
private import AlgToAVCFlow
8+
private import codeql.quantum.experimental.Standardization::Types::KeyOpAlg as KeyOpAlg
89

910
/**
1011
* Given a `KnownOpenSslBlockModeAlgorithmExpr`, converts this to a block family type.
1112
* Does not bind if there is no mapping (no mapping to 'unknown' or 'other').
1213
*/
1314
predicate knownOpenSslConstantToBlockModeFamilyType(
14-
KnownOpenSslBlockModeAlgorithmExpr e, Crypto::TBlockCipherModeOfOperationType type
15+
KnownOpenSslBlockModeAlgorithmExpr e, KeyOpAlg::ModeOfOperationType type
1516
) {
1617
exists(string name |
1718
name = e.(KnownOpenSslAlgorithmExpr).getNormalizedName() and
1819
(
19-
name.matches("CBC") and type instanceof Crypto::CBC
20+
name = "CBC" and type instanceof KeyOpAlg::CBC
2021
or
21-
name.matches("CFB%") and type instanceof Crypto::CFB
22+
name = "CFB%" and type instanceof KeyOpAlg::CFB
2223
or
23-
name.matches("CTR") and type instanceof Crypto::CTR
24+
name = "CTR" and type instanceof KeyOpAlg::CTR
2425
or
25-
name.matches("GCM") and type instanceof Crypto::GCM
26+
name = "GCM" and type instanceof KeyOpAlg::GCM
2627
or
27-
name.matches("OFB") and type instanceof Crypto::OFB
28+
name = "OFB" and type instanceof KeyOpAlg::OFB
2829
or
29-
name.matches("XTS") and type instanceof Crypto::XTS
30+
name = "XTS" and type instanceof KeyOpAlg::XTS
3031
or
31-
name.matches("CCM") and type instanceof Crypto::CCM
32+
name = "CCM" and type instanceof KeyOpAlg::CCM
3233
or
33-
name.matches("GCM") and type instanceof Crypto::GCM
34+
name = "CCM" and type instanceof KeyOpAlg::CCM
3435
or
35-
name.matches("CCM") and type instanceof Crypto::CCM
36-
or
37-
name.matches("ECB") and type instanceof Crypto::ECB
36+
name = "ECB" and type instanceof KeyOpAlg::ECB
3837
)
3938
)
4039
}
@@ -64,10 +63,10 @@ class KnownOpenSslBlockModeConstantAlgorithmInstance extends OpenSslAlgorithmIns
6463
getterCall = this
6564
}
6665

67-
override Crypto::TBlockCipherModeOfOperationType getModeType() {
66+
override KeyOpAlg::ModeOfOperationType getModeType() {
6867
knownOpenSslConstantToBlockModeFamilyType(this, result)
6968
or
70-
not knownOpenSslConstantToBlockModeFamilyType(this, _) and result = Crypto::OtherMode()
69+
not knownOpenSslConstantToBlockModeFamilyType(this, _) and result = KeyOpAlg::OtherMode()
7170
}
7271

7372
// NOTE: I'm not going to attempt to parse out the mode specific part, so returning

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/CipherAlgorithmInstance.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ predicate knownOpenSslConstantToCipherFamilyType(
3333
or
3434
name.matches("CAST5%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::CAST5())
3535
or
36-
name.matches("2DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DoubleDES())
36+
name.matches("2DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DOUBLE_DES())
3737
or
38-
name.matches("3DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::TripleDES())
38+
name.matches("3DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::TRIPLE_DES())
3939
or
4040
name.matches("DES%") and type = KeyOpAlg::TSymmetricCipher(KeyOpAlg::DES())
4141
or
@@ -113,7 +113,7 @@ class KnownOpenSslCipherConstantAlgorithmInstance extends OpenSslAlgorithmInstan
113113
this.(KnownOpenSslCipherAlgorithmExpr).getExplicitKeySize() = result
114114
}
115115

116-
override Crypto::KeyOpAlg::Algorithm getAlgorithmType() {
116+
override KeyOpAlg::AlgorithmType getAlgorithmType() {
117117
knownOpenSslConstantToCipherFamilyType(this, result)
118118
or
119119
not knownOpenSslConstantToCipherFamilyType(this, _) and

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/EllipticCurveAlgorithmInstance.qll

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,22 @@ class KnownOpenSslEllipticCurveConstantAlgorithmInstance extends OpenSslAlgorith
3939
result = this.(Call).getTarget().getName()
4040
}
4141

42-
override Crypto::TEllipticCurveType getEllipticCurveType() {
43-
Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.getParsedEllipticCurveName(), _, result)
42+
override Crypto::EllipticCurveFamilyType getEllipticCurveFamilyType() {
43+
if
44+
Crypto::ellipticCurveNameToKnownKeySizeAndFamilyMapping(this.getParsedEllipticCurveName(), _,
45+
_)
46+
then
47+
Crypto::ellipticCurveNameToKnownKeySizeAndFamilyMapping(this.getParsedEllipticCurveName(), _,
48+
result)
49+
else result = Crypto::OtherEllipticCurveType()
4450
}
4551

4652
override string getParsedEllipticCurveName() {
4753
result = this.(KnownOpenSslAlgorithmExpr).getNormalizedName()
4854
}
4955

5056
override int getKeySize() {
51-
Crypto::ellipticCurveNameToKeySizeAndFamilyMapping(this.(KnownOpenSslAlgorithmExpr)
57+
Crypto::ellipticCurveNameToKnownKeySizeAndFamilyMapping(this.(KnownOpenSslAlgorithmExpr)
5258
.getNormalizedName(), result, _)
5359
}
5460
}

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/HashAlgorithmInstance.qll

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,35 @@ predicate knownOpenSslConstantToHashFamilyType(
1111
exists(string name |
1212
name = e.(KnownOpenSslAlgorithmExpr).getNormalizedName() and
1313
(
14-
name.matches("BLAKE2B") and type instanceof Crypto::BLAKE2B
14+
name = "BLAKE2B" and type instanceof Crypto::BLAKE2B
1515
or
16-
name.matches("BLAKE2S") and type instanceof Crypto::BLAKE2S
16+
name = "BLAKE2S" and type instanceof Crypto::BLAKE2S
1717
or
18-
name.matches("GOST%") and type instanceof Crypto::GOSTHash
18+
name.matches("GOST%") and type instanceof Crypto::GOST_HASH
1919
or
20-
name.matches("MD2") and type instanceof Crypto::MD2
20+
name = "MD2" and type instanceof Crypto::MD2
2121
or
22-
name.matches("MD4") and type instanceof Crypto::MD4
22+
name = "MD4" and type instanceof Crypto::MD4
2323
or
24-
name.matches("MD5") and type instanceof Crypto::MD5
24+
name = "MD5" and type instanceof Crypto::MD5
2525
or
26-
name.matches("MDC2") and type instanceof Crypto::MDC2
26+
name = "MDC2" and type instanceof Crypto::MDC2
2727
or
28-
name.matches("POLY1305") and type instanceof Crypto::POLY1305
28+
name = "POLY1305" and type instanceof Crypto::POLY1305
2929
or
3030
name.matches(["SHA", "SHA1"]) and type instanceof Crypto::SHA1
3131
or
3232
name.matches("SHA_%") and not name.matches(["SHA1", "SHA3-"]) and type instanceof Crypto::SHA2
3333
or
3434
name.matches("SHA3-%") and type instanceof Crypto::SHA3
3535
or
36-
name.matches(["SHAKE"]) and type instanceof Crypto::SHAKE
36+
name = "SHAKE" and type instanceof Crypto::SHAKE
3737
or
38-
name.matches("SM3") and type instanceof Crypto::SM3
38+
name = "SM3" and type instanceof Crypto::SM3
3939
or
40-
name.matches("RIPEMD160") and type instanceof Crypto::RIPEMD160
40+
name = "RIPEMD160" and type instanceof Crypto::RIPEMD160
4141
or
42-
name.matches("WHIRLPOOL") and type instanceof Crypto::WHIRLPOOL
42+
name = "WHIRLPOOL" and type instanceof Crypto::WHIRLPOOL
4343
)
4444
)
4545
}

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/KnownAlgorithmConstants.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,8 @@ string getAlgorithmAlias(string alias) {
210210
}
211211

212212
/**
213-
* Finds aliases of known alagorithms defined by users (through obj_name_add and various macros pointing to this function)
213+
* Holds for aliases of known algorithms defined by users
214+
* (through obj_name_add and various macros pointing to this function).
214215
*
215216
* The `target` and `alias` are converted to lowercase to be of a standard form.
216217
*/
@@ -222,7 +223,7 @@ predicate customAliases(string target, string alias) {
222223
}
223224

224225
/**
225-
* A hard-coded mapping of known algorithm aliases in OpenSsl.
226+
* Holds for a hard-coded mapping of known algorithm aliases in OpenSsl.
226227
* This was derived by applying the same kind of logic foun din `customAliases` to the
227228
* OpenSsl code base directly.
228229
*

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/MACAlgorithmInstance.qll

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ private import experimental.quantum.OpenSSL.Operations.OpenSSLOperations
77
private import AlgToAVCFlow
88

99
class KnownOpenSslMacConstantAlgorithmInstance extends OpenSslAlgorithmInstance,
10-
Crypto::MACAlgorithmInstance instanceof KnownOpenSslMacAlgorithmExpr
10+
Crypto::MacAlgorithmInstance instanceof KnownOpenSslMacAlgorithmExpr
1111
{
1212
OpenSslAlgorithmValueConsumer getterCall;
1313

@@ -39,14 +39,14 @@ class KnownOpenSslMacConstantAlgorithmInstance extends OpenSslAlgorithmInstance,
3939
result = this.(Call).getTarget().getName()
4040
}
4141

42-
override Crypto::TMACType getMacType() {
43-
this instanceof KnownOpenSslHMacAlgorithmExpr and result instanceof Crypto::THMAC
42+
override Crypto::MacType getMacType() {
43+
this instanceof KnownOpenSslHMacAlgorithmExpr and result = Crypto::HMAC()
4444
or
45-
this instanceof KnownOpenSslCMacAlgorithmExpr and result instanceof Crypto::TCMAC
45+
this instanceof KnownOpenSslCMacAlgorithmExpr and result = Crypto::CMAC()
4646
}
4747
}
4848

49-
class KnownOpenSslHMacConstantAlgorithmInstance extends Crypto::HMACAlgorithmInstance,
49+
class KnownOpenSslHMacConstantAlgorithmInstance extends Crypto::HmacAlgorithmInstance,
5050
KnownOpenSslMacConstantAlgorithmInstance
5151
{
5252
override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() {

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/PaddingAlgorithmInstance.qll

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ private import experimental.quantum.OpenSSL.AlgorithmInstances.KnownAlgorithmCon
55
private import AlgToAVCFlow
66
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.DirectAlgorithmValueConsumer
77
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumerBase
8+
private import codeql.quantum.experimental.Standardization::Types::KeyOpAlg as KeyOpAlg
89

910
/**
1011
* A class to define padding specific integer values.
@@ -28,18 +29,18 @@ class OpenSslPaddingLiteral extends Literal {
2829
* Does not bind if there is no mapping (no mapping to 'unknown' or 'other').
2930
*/
3031
predicate knownOpenSslConstantToPaddingFamilyType(
31-
KnownOpenSslPaddingAlgorithmExpr e, Crypto::TPaddingType type
32+
KnownOpenSslPaddingAlgorithmExpr e, KeyOpAlg::PaddingSchemeType type
3233
) {
3334
exists(string name |
3435
name = e.(KnownOpenSslAlgorithmExpr).getNormalizedName() and
3536
(
36-
name.matches("OAEP") and type = Crypto::OAEP()
37+
name = "OAEP" and type = KeyOpAlg::OAEP()
3738
or
38-
name.matches("PSS") and type = Crypto::PSS()
39+
name = "PSS" and type = KeyOpAlg::PSS()
3940
or
40-
name.matches("PKCS7") and type = Crypto::PKCS7()
41+
name = "PKCS7" and type = KeyOpAlg::PKCS7()
4142
or
42-
name.matches("PKCS1V15") and type = Crypto::PKCS1_v1_5()
43+
name = "PKCS1V15" and type = KeyOpAlg::PKCS1_V1_5()
4344
)
4445
)
4546
}
@@ -85,7 +86,7 @@ class KnownOpenSslPaddingConstantAlgorithmInstance extends OpenSslAlgorithmInsta
8586
// Source is `this`
8687
src.asExpr() = this and
8788
// This traces to a padding-specific consumer
88-
RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow::flow(src, sink)
89+
RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow::flow(src, sink)
8990
) and
9091
isPaddingSpecificConsumer = true
9192
}
@@ -98,24 +99,24 @@ class KnownOpenSslPaddingConstantAlgorithmInstance extends OpenSslAlgorithmInsta
9899

99100
override OpenSslAlgorithmValueConsumer getAvc() { result = getterCall }
100101

101-
Crypto::TPaddingType getKnownPaddingType() {
102-
this.(Literal).getValue().toInt() in [1, 7, 8] and result = Crypto::PKCS1_v1_5()
102+
KeyOpAlg::PaddingSchemeType getKnownPaddingType() {
103+
this.(Literal).getValue().toInt() in [1, 7, 8] and result = KeyOpAlg::PKCS1_V1_5()
103104
or
104-
this.(Literal).getValue().toInt() = 3 and result = Crypto::NoPadding()
105+
this.(Literal).getValue().toInt() = 3 and result = KeyOpAlg::NoPadding()
105106
or
106-
this.(Literal).getValue().toInt() = 4 and result = Crypto::OAEP()
107+
this.(Literal).getValue().toInt() = 4 and result = KeyOpAlg::OAEP()
107108
or
108-
this.(Literal).getValue().toInt() = 5 and result = Crypto::ANSI_X9_23()
109+
this.(Literal).getValue().toInt() = 5 and result = KeyOpAlg::ANSI_X9_23()
109110
or
110-
this.(Literal).getValue().toInt() = 6 and result = Crypto::PSS()
111+
this.(Literal).getValue().toInt() = 6 and result = KeyOpAlg::PSS()
111112
}
112113

113-
override Crypto::TPaddingType getPaddingType() {
114+
override KeyOpAlg::PaddingSchemeType getPaddingType() {
114115
isPaddingSpecificConsumer = true and
115116
(
116117
result = this.getKnownPaddingType()
117118
or
118-
not exists(this.getKnownPaddingType()) and result = Crypto::OtherPadding()
119+
not exists(this.getKnownPaddingType()) and result = KeyOpAlg::OtherPadding()
119120
)
120121
or
121122
isPaddingSpecificConsumer = false and
@@ -143,7 +144,7 @@ class KnownOpenSslPaddingConstantAlgorithmInstance extends OpenSslAlgorithmInsta
143144
// this instanceof Literal and
144145
// this.getValue().toInt() in [0, 1, 3, 4, 5, 6, 7, 8]
145146
// // TODO: trace to padding-specific consumers
146-
// RSAPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow
147+
// RsaPaddingAlgorithmToPaddingAlgorithmValueConsumerFlow
147148
// }
148149
// override string getRawPaddingAlgorithmName() { result = this.(Literal).getValue().toString() }
149150
// override Crypto::TPaddingType getPaddingType() {
@@ -161,18 +162,18 @@ class KnownOpenSslPaddingConstantAlgorithmInstance extends OpenSslAlgorithmInsta
161162
// else result = Crypto::OtherPadding()
162163
// }
163164
// }
164-
class OAEPPaddingAlgorithmInstance extends Crypto::OAEPPaddingAlgorithmInstance,
165+
class OaepPaddingAlgorithmInstance extends Crypto::OaepPaddingAlgorithmInstance,
165166
KnownOpenSslPaddingConstantAlgorithmInstance
166167
{
167-
OAEPPaddingAlgorithmInstance() {
168-
this.(Crypto::PaddingAlgorithmInstance).getPaddingType() = Crypto::OAEP()
168+
OaepPaddingAlgorithmInstance() {
169+
this.(Crypto::PaddingAlgorithmInstance).getPaddingType() = KeyOpAlg::OAEP()
169170
}
170171

171-
override Crypto::HashAlgorithmInstance getOAEPEncodingHashAlgorithm() {
172+
override Crypto::HashAlgorithmInstance getOaepEncodingHashAlgorithm() {
172173
none() //TODO
173174
}
174175

175-
override Crypto::HashAlgorithmInstance getMGF1HashAlgorithm() {
176+
override Crypto::HashAlgorithmInstance getMgf1HashAlgorithm() {
176177
none() //TODO
177178
}
178179
}

cpp/ql/lib/experimental/quantum/OpenSSL/AlgorithmInstances/SignatureAlgorithmInstance.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class KnownOpenSslSignatureConstantAlgorithmInstance extends OpenSslAlgorithmIns
7373
none()
7474
}
7575

76-
override KeyOpAlg::Algorithm getAlgorithmType() {
76+
override KeyOpAlg::AlgorithmType getAlgorithmType() {
7777
knownOpenSslConstantToSignatureFamilyType(this, result)
7878
or
7979
not knownOpenSslConstantToSignatureFamilyType(this, _) and

0 commit comments

Comments
 (0)