-
Notifications
You must be signed in to change notification settings - Fork 43
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
Support for authentication with client certificates #656
base: develop
Are you sure you want to change the base?
Support for authentication with client certificates #656
Conversation
@microsoft-github-policy-service agree company="Seadex GmbH" |
Using EIS (edge identity service) to retrieve the x509 certs is already a supported way to use them for individual and group DPS enrollments. If I am interpreting correctly, what is the reasoning to support yet another way of doing it directly? Is it because you do not want to use EIS? You can see here that we request the certificate from EIS unix domain server:
Specifically, we are using https://azure.github.io/iot-identity-service/ with further details here: https://azure.github.io/iot-identity-service/api/identity-service.html The reason device update agent uses EIS is for separating and offloading the concerns of securely handling the certicates where EIS can use a TPM or HSM, etc. |
Yes.
Yes, because it is rather heavy-weight for our environment. If we do the same with EIS it's even more binaries to build, configure, and maintain. Side note: Also, the system is constrained in memory and flash storage. It's not a tiny embedded controller. But adding a bunch of services just to do certificate-based authentication (which works without them) is not justified.
This is already implemented for our device and works just fine with OpenSSL out of the box. You don't point to a key file in the filesystem, but you use a "special" path to the key in the secure storage. Thus, there is no benefit for us. But a lot of overhead. Especially, because the device software already uses the Azure IoT C SDK and uses the "direct way" to authenticate using the same key material. It works well and it's completely secure. |
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.
Looks good. A couple of minor comments.
if (x509File) | ||
{ | ||
if (fseek(x509File, 0, SEEK_END) == 0) { | ||
bufferLength = (size_t)ftell(x509File); |
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.
Somewhat pedantic but how about handling when ftell returns -1 and Log_Error the errno. I suppose it would handle mostly the off chance when x509File is not a regular file (such as ESPIPE)
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.
done
bytesRead = fread(buffer, 1, bufferLength, x509File); | ||
if (bytesRead < bufferLength) | ||
{ | ||
Log_Error("Failed to read '%s'!", x509_file_path); |
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.
could eof and error be distinguished and also log the return value from bytesRead? Something like this:
if (bytesRead < bufferLength)
{
if (feof(x509File)) {
Log_Error("Unexpected end of file while reading '%s', bytesRead: %zu", x509_file_path, bytesRead);
} else if (ferror(x509File)) {
Log_Error("Error occurred while reading '%s', bytesRead: %zu", x509_file_path, bytesRead);
}
free(buffer);
buffer = 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.
done
9ed2796
to
41ec48b
Compare
Updated the PR with the suggest changes. |
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.
mostly style comments, some of which can be fixed by running clang-format.
if (connection_x509_cert_file_path || connection_x509_private_key_file_path || connection_x509_ca_cert_file_path) { | ||
if(connection_x509_cert_file_path && connection_x509_private_key_file_path && connection_x509_ca_cert_file_path) { | ||
agent->x509Cert = ADUC_AgentInfo_Read_X509_File(connection_x509_cert_file_path); | ||
if (!agent->x509Cert) |
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.
the code base uses explicit conditions regarding check for NULL values, so please change to:
if (agent->x509Cert == NULL)
The same for lines 221 and 226.
FILE* x509File = NULL; | ||
|
||
x509File = fopen(x509_file_path, "rb"); | ||
if (x509File) |
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.
use explicit checks for NULL / non-NULL:
if (x509File != NULL)
bytesRead = fread(buffer, 1, bufferLength, x509File); | ||
if (bytesRead < bufferLength) | ||
{ | ||
if (feof(x509File)) |
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.
Line 95 here has an if-statement that is 7 levels deep.
Let's keep the nesting of if-statements shallow by using the following pattern that:
- guarantees usually just 1-level of nesting, and usually at most 2-3 nesting depth in practice
- Centralizes all clean-up code after 'done' label that in general avoids doing so in multiple nested error branches (not the case here but in general it lowers the cognitive load), similar in function to Zig or Odin's defer statement (or c++ RAII) but can control exactly when it happens:
bool success = false;
...
if (x509File == NULL)
{
Log_Error("Failed to open '%s'!", x509_file_path);
goto done;
}
if (fseek(x509File, 0, SEEK_END) != 0)
{
Log_Error("Failed to seek end of '%s'!", x509_file_path);
goto done;
}
...
...
success = true;
done:
if (!success)
{
free(buffer);
buffer = NULL;
}
if (x509File != NULL)
{
fclose(x509File);
}
return buffer;
}
x509File = fopen(x509_file_path, "rb"); | ||
if (x509File) | ||
{ | ||
if (fseek(x509File, 0, SEEK_END) == 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.
Style consistency issue: Opening brace should be on next line
Touch every file in the current set of commits, git add
them to git staging, and then run scripts/clang-format.sh
. see:
if [[ $0 != "${BASH_SOURCE[0]}" ]]; then |
Longer-term, we need to add clang-format.sh (and some other things) to the git pre-commit hook. Currently, the existing git pre-commit hook only runs sh-format.sh (
if ! "$GITROOT/scripts/sh-format.sh" -v; then |
Log_Error("Failed to open '%s'!", x509_file_path); | ||
} | ||
|
||
return(buffer); |
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.
style: no parens for return
Log_Error("Failed to seek end of '%s'!", x509_file_path); | ||
} | ||
|
||
if(fclose (x509File) != 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.
style: no space between function name and arg list (should be caught by clang-format)
{ | ||
Log_Error("Unexpected end of file while reading '%s' (bytesRead: %zu)!", x509_file_path, bytesRead); | ||
} | ||
else if (ferror(x509File)) { |
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.
paren on next line
} | ||
|
||
if(fclose (x509File) != 0) { | ||
Log_Warn("Failed to close '%s'!", x509_file_path); |
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.
omit. no need to log when fclose fails as it's not really actionable and the log trace might not even be able to flush to disk in that case (things are in a pretty bad state)
Log_Error("Failed to seek end of '%s'!", x509_file_path); | ||
} | ||
|
||
if(fclose (x509File) != 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.
would prefer this to be in centralized cleanup area, such as after 'done:' label (along with free(buffer) if in error condition).
{ | ||
Log_Error("Failed to read '%s' (bytesRead: %zu)!", x509_file_path, bytesRead); | ||
} | ||
free(buffer); |
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.
for future maintainability, this should be done unconditionally at the end, if in "not succeeded" state.
Added possibility to authenticate via client certificates (including configuration of CA certificates).