-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Quote when appending username and password to the ODBC connection string #8307
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
Conversation
Thank you for the PR! Some thoughts:
vs.
This documentation does not specify how to treat braces in quoted strings; we're likely on the safe side if we handle that according to .NET (i.e. double the braces)
|
Probably a good idea. I think. An
Right. The code sharing potential is obvious, it just seems silly to have to put it in
I had a prototype that allocated a buffer, but there'd have to be some untangling of the logic to deal with i.e. failed allocations and such. |
ext/pdo_odbc/odbc_driver.c
Outdated
} | ||
snprintf(dsn, new_dsn_size, "%s;UID=%s;PWD=%s", dbh->data_source, uid, pwd); | ||
pefree((char*)dbh->data_source, dbh->is_persistent); | ||
dbh->data_source = dsn; | ||
/* Convoluted label to handle freeing in this scope */ | ||
connection_string_fail: |
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.
Yeah, that's how we usually deal with deallocation needs. It may make sense to use alloca()
instead of emalloc()
for performance reasons, but that would not avoid the manual cleanup, for portability reasons; see
php-src/Zend/zend_portability.h
Lines 348 to 365 in 6828f9d
#if (defined(HAVE_ALLOCA) || (defined (__GNUC__) && __GNUC__ >= 2)) && !(defined(ZTS) && defined(HPUX)) && !defined(DARWIN) | |
# define ZEND_ALLOCA_MAX_SIZE (32 * 1024) | |
# define ALLOCA_FLAG(name) \ | |
bool name; | |
# define SET_ALLOCA_FLAG(name) \ | |
name = 1 | |
# define do_alloca_ex(size, limit, use_heap) \ | |
((use_heap = (UNEXPECTED((size) > (limit)))) ? emalloc(size) : alloca(size)) | |
# define do_alloca(size, use_heap) \ | |
do_alloca_ex(size, ZEND_ALLOCA_MAX_SIZE, use_heap) | |
# define free_alloca(p, use_heap) \ | |
do { if (UNEXPECTED(use_heap)) efree(p); } while (0) | |
#else | |
# define ALLOCA_FLAG(name) | |
# define SET_ALLOCA_FLAG(name) | |
# define do_alloca(p, use_heap) emalloc(p) | |
# define free_alloca(p, use_heap) efree(p) | |
#endif |
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.
I've always been hesitant to use alloca
for the reasons people usually are (i.e. failure when not enough stack?). If you still have to free, it clobbers a lot of the ergonomics of it too...
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.
Yeah, VLAs would be a nice solution, if they we're available "everywhere".
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.
I think technically, VLAs are worse in the "failure is hard to deal with" department than alloca
. It's really unfortunate C doesn't have similar facilities to C++ in terms of scoped (de)allocations/RAII.
So regarding code sharing, where would be the best place to put it? I assume since ODBC extensions are bundled, it can be safe to put it in i.e. |
Sorry for the bump, still a bit unclear on what's the best place to put it still, especially for a kind of obscure piece of functionality. I guess WRT:
Exposing an API like |
Indeed, this looks like the most suitable place. The filename php_odbc_utils.c looks like a good choice either; maybe more stuff can be put in there in the future. I think the php_odbc_utils.h file needs to be exported (
It should be possible to do phpize builds of ext/odbc and ext/pdo_odbc, and I think that might be standard for several Linux distros. Although, that shouldn't be a problem, if the functions are exported and the headers available.
Oh, right. It might make sense to expose that function to userland. |
It seems at least in The thing with exposing it to userspace is the semantics. It might make sense to expose should and is quoted too, so userspace can determine it themselves. Of course, if the code's in |
f5337d3
to
c542668
Compare
Two more thoughts:
|
Yes, definitely. Usually, these notes are not part of the PR (mostly because that easily can get stale), but feel free to add a respective commit (or just post the upgrading notes in a comment, so the "merger" can finally add these to UPGRADING).
I tend to say no, because that would cause different code behavior based on an INI setting. And if we target "master", the need for backward compatibility isn't that strong (besides that the changes likely only affect few users). |
I think I'm confident where this PR stands now; it should be ready for review now. |
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.
Thank you! I think I'm mostly (I left some comments) fine with this, but would like to get more input from others, especially since we're introducing new userland functions and the module in main.
I've removed the defensive checks as you sugest. I can understand concerns about new functionality in |
I'm really not a fan of having this in ext/standard, ext/odbc maybe. I don't really like the fact this resides in main either, but don't have a good solution against that. |
I can see that - I just put it there because it lives in
TBH, me neither, since it's quite ODBC specific, but there's not many places where extensions can share code other than that. |
Sorry for bumping, but it's been a week. I've moved the functions out of |
Because the UID= and PWD= values are appended to the SQLDriverConnect case when credentials are passed, we have to append them to the string in case users are relying on this behaviour. However, they must be quoted, or the arguments will be invalid (or possibly more injected). This means users had to quote arguments or append credentials to the raw connection string themselves. It seems that ODBC quoting rules are consistent enough (and that Microsoft trusts them enough to encode into the .NET BCL) that we can actually check if the string is already quoted (in case a user is already quoting because of this not being fixed), and if not, apply the appropriate ODBC quoting rules. This has been tested with the MariaDB and IBM i Db2 drivers. There are still some questions: - Procedural ODBC, with code sharing? - Are we paying attention to all of the quoting rules? i.e. any other special characters? We don't need to care if ; are in the strings at least, since we always quote now. - Should we always quote? Could it break some drivers? If so, could it be an INI option? - Buffer length/truncation behaviour (since the quoted string is on stack) - How do we unit test this?
We might not have to do the work if we don't have to. cmb points out that the rules are a little contradictory, and to me, seem unnecessary for some special characters, but it might be worth a shot.
Also handle freeing. No RAII in C, so there's some convolution in how we free the variables in this scope. Let me know if there's a better way.
I think the intent with the switch was special handling for the quotes, but then I realize you didn't need it if it's already quoted. Makes the suggested change from cmb.
Handles non-null UID with null PWD crash. Also is a bit more strict with the strstr to find an existing UID/PWD set in the connection string.
Right now, it just blithely duplicates the functions backing it, and the logic has been adapted to fit the procedural ODBC one. Putting this in `main/` or something would be a good idea to avoid having to keep this function in multiple places. I see some potential for more sharing and alteration of the rules in both though.
As of right now, only the connection string quoting rules, but perhaps other functionality can be moved in as needed. I'm not sure if main/ is the best place, but it seems other functionality only really used by extensions is here, so it makes sense.
This is because the code exists in main/, and are shared between both ODBC extensions, so it doesn't make sense for it to only exist in one or the other. There may be a better spot for it.
The primary reason for these functions oother than for i.e. database frameworks and libraries. It's much easier to test them outside of the context of needing a full ODBC driver.
Per GPB's suggestion. It does make sense, even if the functionality is common to both extensions.
74291c4
to
311b1c1
Compare
Sorry for the bump again, but I've rebased. I've also heard about the 8.2 feature freeze in a few months, so I would like to try to get this in before that, particularly if this has security repercussions. |
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.
Thanks for your work, and the pinging! :)
Except for the minor issue pointed out below, this looks good to me. I'm not too happy to have the functions in ext/odbc (since they may be useful for PDO_ODBC users as well), but we discussed that already, and it appears to be the best compromise (for now).
If there are no objections, I'll merge this on Friday.
This PR was merged into the 1.28-dev branch. Discussion ---------- Add odbc_connection_string_*() functions This PR adds ODBC functions added in php/php-src#8307. Commits ------- 1ec3314 Add odbc_connection_string_*() functions
Because the UID= and PWD= values are appended to the
SQLDriverConnect
case when credentials are passed, we have to append them to the string in case users are relying on this behaviour. However, they must be quoted, or the arguments will be invalid (or possibly more injected). This means users had to quote arguments or append credentials to the raw connection string themselves.It seems that ODBC quoting rules are consistent enough (and that Microsoft trusts them enough to encode into the .NET BCL) that we can actually check if the string is already quoted (in case a user is already quoting because of this not being fixed), and if not, apply the appropriate ODBC quoting rules.
This has been tested with the MariaDB and IBM i Db2 drivers.
There are still some questions:
Fixes GH-8300