-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add modified certificate DNS check #1
base: master
Are you sure you want to change the base?
Conversation
tlscommon.c
Outdated
@@ -626,13 +626,38 @@ static int certattr_matchip(GENERAL_NAME *gn, struct certattrmatch *match){ | |||
&& !memcmp(ASN1_STRING_get0_data(gn->d.iPAddress), &match->ipaddr, l)) ? 1 : 0 ; | |||
} | |||
|
|||
static int compareWithModifiedHostname(char* certDNS, char* scriptHostName) { | |||
int len = 0; | |||
int len1 = 0, len2 = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KarthikaRajendran11 , these variables' names could be more meaningful. The current ones make it hard to follow the logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has this been tested with multiple DNS names in the certificate? For example, SingleDigits contains 3af521.net, *.3af521.net, 3af521.com, *.3af521.com
in the SAN.
If the host name returned by the script is idp.3af521.net, then it will match only to *.3af521.net and idp.3af521.net |
tlscommon.c
Outdated
char* pos; | ||
char* modifiedScriptHostName; | ||
char *result; | ||
char generic[] = "*"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add null checks for the certDNS, and scriptHostName pointers. Initialize each variable (pos, modifiedScriptHostName, and result) to a known value, specifically NULL.
tlscommon.c
Outdated
return 0; | ||
} | ||
len = strlen(pos); | ||
modifiedScriptHostName = malloc(len + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(1) Use calloc() instead (it creates a zero'd memory buffer instead of random bytes that malloc gives you).
(2) Check for memory allocation failures. Specifically malloc/calloc return NULL when the system is out of memory, check for this, and bail.
(3) You can eliminate L646 as well, since the array will be all zeros.
tlscommon.c
Outdated
len = strlen(pos); | ||
modifiedScriptHostName = malloc(len + 1); | ||
memcpy(modifiedScriptHostName, pos, len); | ||
modifiedScriptHostName[len] = '\0'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove (per earlier comment)
tlscommon.c
Outdated
modifiedScriptHostName = malloc(len + 1); | ||
memcpy(modifiedScriptHostName, pos, len); | ||
modifiedScriptHostName[len] = '\0'; | ||
debug(DBG_DBG, "modifiedScriptHostName : %s", modifiedScriptHostName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
modifiedScriptHostName is an attacker (think "specially crafted malicious certificate") controlled. Let's not print out the certificate info, unless we REALLY need to.
tlscommon.c
Outdated
debug(DBG_DBG, "modifiedScriptHostName : %s", modifiedScriptHostName); | ||
lenOfGeneric = strlen(generic); | ||
lenOfModifiedScriptHostName = strlen(modifiedScriptHostName); | ||
result = malloc(lenOfGeneric + lenOfModifiedScriptHostName + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, use calloc() here, and check for out of memory conditions.
tlscommon.c
Outdated
result = malloc(lenOfGeneric + lenOfModifiedScriptHostName + 1); | ||
memcpy(result, generic, lenOfGeneric); | ||
memcpy(result + lenOfGeneric, modifiedScriptHostName, lenOfModifiedScriptHostName + 1); | ||
if (strlen(certDNS) == strlen(result) && memcmp(result, certDNS, strlen(certDNS)) == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My preferred way to code this would be. It's less likely to leak memory as changes are made to the code base over time.
if (strlen(...) == strlen(...)....) {
ret = 1
} else {
ret = 0
}
if (result) {
free(result);
result = NULL;
}
if (modifiedScriptHostName) {
free(modifiedScriptHostName);
modifiedScriptHostName = NULL;
}
return ret;
tlscommon.c
Outdated
lenOfModifiedScriptHostName = strlen(modifiedScriptHostName); | ||
result = malloc(lenOfGeneric + lenOfModifiedScriptHostName + 1); | ||
memcpy(result, generic, lenOfGeneric); | ||
memcpy(result + lenOfGeneric, modifiedScriptHostName, lenOfModifiedScriptHostName + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the +1 at the end, since calloc will give you a zero'd array. If for some reason modifiedScriptHostName isn't null terminated, then you're not corrupting the result buffer along the way. This guarantees that result will be null terminated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered the following certificate/DNS testing issues?
Please read https://datatracker.ietf.org/doc/html/rfc6125#section-6.4
- DNS comparisons are case insensitive ASCII, the code looks like
idp.EXAMPLE.COM
won't match to*.example.com
and it should. - Wildcard certificates cannot match on strings like "bar.*.example.net". Only the first level should be compared.
- Internationalized Domain Names have special rules, and pre-processing requirements.
@@ -568,7 +543,6 @@ vY/uPjA=\n\ | |||
X509_free(certsanipv6); | |||
X509_free(certsanipv6indns); | |||
X509_free(certcomplex); | |||
X509_free(certcomplexother); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this certificate removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not being used in any tests. They are just allocating it and freeing
static int _general_name_regex_match(char *v, int l, struct certattrmatch *match) { | ||
char *s; | ||
if (l <= 0 ) | ||
return 0; | ||
if (match->exact) { | ||
if (l == strlen(match->exact) && memcmp(v, match->exact, l) == 0) | ||
return 1; | ||
if(compareWithModifiedHostname(v, match->exact)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this in the match->exact
block? If it must be here, then please include a comment on why since it mutates the expected behaviour of an exact
match.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the original code format. If match->exact is set it means we have found a host/DNS (the script has returned a valid host).
tlscommon.c
Outdated
@@ -626,13 +628,182 @@ static int certattr_matchip(GENERAL_NAME *gn, struct certattrmatch *match){ | |||
&& !memcmp(ASN1_STRING_get0_data(gn->d.iPAddress), &match->ipaddr, l)) ? 1 : 0 ; | |||
} | |||
|
|||
static char* copy(char* s1, char* s2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This func is not copy
. It is allocating & concatenating.
tlscommon.c
Outdated
if(s1 == NULL && s2 == NULL) { | ||
return NULL; | ||
} | ||
char* result = (char*) calloc(strlen(s1) + strlen(s2), sizeof(char)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off by one error, should be strlen(s1)+strlen(s2)+1.
tlscommon.c
Outdated
if(s2 == NULL) { | ||
return s1; | ||
} | ||
if(s1 == NULL && s2 == NULL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be the first boolean test made.
tlscommon.c
Outdated
@@ -626,13 +628,185 @@ static int certattr_matchip(GENERAL_NAME *gn, struct certattrmatch *match){ | |||
&& !memcmp(ASN1_STRING_get0_data(gn->d.iPAddress), &match->ipaddr, l)) ? 1 : 0 ; | |||
} | |||
|
|||
static char* copy(char* s1, char* s2) { | |||
if(s1 == NULL) { | |||
return s2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dangerous, this breaks the schematics of the function. Returning s2 doesn't return a deep copy (or clone) of s2, but a pointer to s2. Doing do would cause the memory to be improperly deallocated as shown below...
char* mynewstring = copy("Foo", "Bar");
// use mynewstring for a while, then deallocate it.
free(mynewstring);
mynewstring=NULL;
char* thisisbad = copy(NULL, "Foo");
// use thisisbad
free(thisisbad); // Attempt to free the pointer to the string "Foo" which ISN'T heap allocated.
You should allocate new memory for "Foo" from the heap (with calloc) and then copy it over.
tlscommon.c
Outdated
char* result = NULL; | ||
for(i = 0; i < n; i++) { | ||
val = va_arg(ap, char*); | ||
result = copy(result, val); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is result
free'd? What happens if it's NULL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
append is called from compareWithModifiedHostname and calculateLenOfLabel (which is called from compareWithModifiedHostname). NULL check is done in compareWithModifiedHostname. And the memory used is also freed in compareWithModifiedHostname. modifiedHostName, pattern, lenRegex are the variables that are results of append. They are freed in compareWithModifiedHostname.
tlscommon.c
Outdated
|
||
int reti = regcomp(®ex, pattern, REG_EXTENDED); | ||
if (reti) { | ||
regerror(reti, ®ex, buffer, 100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of using 100
the constant, should be sizeof(buffer)
instead
tlscommon.c
Outdated
return result; | ||
} | ||
int lenOfFirstPortion = strlen(dns) - strlen(firstPos); | ||
result = (char*) calloc(lenOfFirstPortion, sizeof(char)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off by 1, lenOfFirstPortion + 1
char* result = NULL; | ||
for(i = 0; i < n; i++) { | ||
val = va_arg(ap, char*); | ||
result = concatenate(result, val); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does result
get free()
'd
tlscommon.c
Outdated
|
||
static char* calculateLenOfLabel(int len) { | ||
char str[3]; | ||
sprintf(str, "%d", len); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Buffer overflow here. len can (in general) be the size of an integer. So you need more than 3 bytes to store a 32-bit decimal value.
char str[3]; | ||
sprintf(str, "%d", len); | ||
debug(DBG_DBG, "str : %s", str); | ||
char* result = append(3, "{0,", str, "}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is 3 really what we want?
// Internationlized Domain Names: www.äöü.com converted to www.xn--4ca0bs.com. xn--4ca0bs is considered | ||
// a valid DN and let through. diseñolatinoamericano.com => xn--diseolatinoamericano-66b.com will also be | ||
// let through. So IDNs are passed in Punycode encoded and xn-- says everything that follows is unicode encoded. | ||
static int compareWithModifiedHostname(char* certDNS, char* hostname) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Karthika, there is a much safer memory pattern to use here. The reason for the pattern below is that it keeps all memory deallocations in one place for better readability, and better maintainability. This is 'C' version of exception handling (sort of). Consider the following pattern...
char* firstPorCertDNS = NULL;
char* firstPorHostName = NULL;
char* modifiedHostName = NULL;
int retcode = ERROR;
....
do stuff
....
if (I detected and error) {
goto cleanup;
}
... do more stuff ...
retcode = 1;
return retcode;
cleanup:
if (firstPorCertDNS != NULL) {
free(firstPorCertDNS);
firstPorCertDNS = NULL;
}
if (firstPorHostName != NULL) {
free(firstPorHostName);
firstPorHostName = NULL;
}
if (modifiedHostName != NULL) {
free(modifiedHostName);
modifiedHostName = NULL;
}
return retcode;
}
retCode = 1; | ||
} | ||
|
||
goto cleanup; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unneeded & spacing below is off. Labels are outdented.
No description provided.