Skip to content

Commit bc9e41a

Browse files
1. Fix bug in ocrc.c#combinecredentials where a null user+pwd
generates garbage. This in turn interferes with using .netrc because the garbage user+pwd can will override the .netrc. Note that this may work ok sometimes if the garbage happens to start with a nul character. 2. It turns out that the user:pwd combination needs to support character escaping. One reason is the user may contain an '@' character. The other is that modern password rules make it not unlikely that the password will contain characters that interfere with url parsing. So, the rule I have implemented is that all occurrences of the user:pwd format must escape any dodgy characters. The escape format is URL escaping of the form %XX. This applies both to user:pwd embedded in a URL as well as the use of HTTP.CREDENTIALS.USERPASSWORD in a .dodsrc/.daprc file. The user and password in .netrc must not be escaped. This is now documented in docs/auth.md The fix for #2 actually obviated #1. Now, internally, the user and pwd are stored separately and not in the user:pwd format. They are combined (and escaped) only when needed.
1 parent e226cc7 commit bc9e41a

File tree

16 files changed

+306
-150
lines changed

16 files changed

+306
-150
lines changed

docs/auth.md

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
netCDF Authorization Support
2-
============================
2+
======================================
33
<!-- double header is needed to workaround doxygen bug -->
44

55
# netCDF Authorization Support {#Header}
@@ -38,8 +38,11 @@ This username and password will be used if the server asks for
3838
authentication. Note that only simple password authentication
3939
is supported in this format.
4040
Specifically note that [redirection-based](#REDIR)
41-
authorization will not work with this because the username and password
42-
will only be used on the initial request, not the redirection
41+
authorization may not work with this because the username and password
42+
will only be used on the initial request, not the redirection.
43+
Note also that the `user:password` form may contain characters that must be
44+
escaped. See the <a href="#USERPWDESCAPE">password escaping</a> section to see
45+
how to properly escape the user and password.
4346

4447
## RC File Authentication {#DODSRC}
4548
The netcdf library supports an _rc_ file mechanism to allow the passing
@@ -134,6 +137,27 @@ specifies the absolute path of the .netrc file.
134137
See [redirection authorization](#REDIR)
135138
for information about using .netrc.
136139

140+
## Password Escaping {#USERPWDESCAPE}
141+
With current password rules, it is is not unlikely that the password
142+
will contain characters that need to be escaped. Similarly, the user
143+
may contain characters such as '@' that need to be escaped. To support this,
144+
it is assumed that all occurrences of `user:`password` use URL (i.e. %%XX)
145+
escaping for at least the characters in the table below.
146+
Note that escaping must be used when the user+pwd is embedded in the URL.
147+
It must also be used when the user+pwd is specified in the `.dodsrc/.daprc` file
148+
via HTTP.CREDENTIALS.USERPASSWORD.
149+
Escaping should not be used in the `.netrc` file.
150+
151+
The relevant characters and their escapes are as follows.
152+
<table>
153+
<tr><th>Character</th><th>Escaped Form</th>
154+
<tr><td>'@'</td><td>%40</td>
155+
<tr><td>':'</td><td>%3a</td>
156+
<tr><td>'?'</td><td>%3f</td>
157+
<tr><td>'#'</td><td>%23</td>
158+
<tr><td>'/'</td><td>%2f</td>
159+
</table>
160+
137161
## Redirection-Based Authentication {#REDIR}
138162

139163
Some sites provide authentication by using a third party site
@@ -150,16 +174,18 @@ using the _https_ protocol (note the use of _https_ instead of _http_).
150174
4. URS sends a redirect (with authorization information) to send
151175
the client back to the SOI to actually obtain the data.
152176

153-
It turns out that libcurl uses the password in the `.daprc`
154-
file (or from the url)
155-
only for the initial connection. This causes problems because
156-
the redirected connection is the one that actually requires the password.
157-
This is where the `.netrc` file comes in. Libcurl will use `.netrc` for
158-
the redirected connection. It is possible to cause libcurl to use
159-
the `.daprc` password always, but this introduces a security hole
160-
because it may send the initial user+pwd to the redirection site.
161-
In summary, if you are using redirection, then you must create a `.netrc`
162-
file to hold the password for the site to which the redirection is sent.
177+
It turns out that libcurl, by default, uses the password in the
178+
`.daprc` file (or from the url) for all connections that request
179+
a password. This causes problems because only the the specific
180+
redirected connection is the one that actually requires the password.
181+
This is where the `.netrc` file comes in. Libcurl will use `.netrc`
182+
for the redirected connection. It is possible to cause libcurl
183+
to use the `.daprc` password always, but this introduces a
184+
security hole because it may send the initial user+pwd to every
185+
server in the redirection chain.
186+
In summary, if you are using redirection, then you are
187+
''strongly'' encouraged to create a `.netrc` file to hold the
188+
password for the site to which the redirection is sent.
163189

164190
The format of this `.netrc` file will contain lines that
165191
typically look like this.
@@ -170,11 +196,14 @@ where the machine, mmmmmm, is the hostname of the machine to
170196
which the client is redirected for authorization, and the
171197
login and password are those needed to authenticate on that machine.
172198

173-
The `.netrc` file can be specified by
199+
The location of the `.netrc` file can be specified by
174200
putting the following line in your `.daprc`/`.dodsrc` file.
175201

176202
HTTP.NETRC=<path to netrc file>
177203

204+
If not specified, then libcurl will look first in the current
205+
directory, and then in the HOME directory.
206+
178207
One final note. In using this, you MUST
179208
to specify a real file in the file system to act as the
180209
cookie jar file (HTTP.COOKIEJAR) so that the

include/ncuri.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
#define NCU_ECONSTRAINTS (11)
2222

2323
/* Define flags to control what is included by ncuribuild*/
24-
#define NCURIPATH 1
25-
#define NCURIPWD 2
26-
#define NCURIQUERY 4
27-
#define NCURIFRAG 8
28-
#define NCURIENCODE 16 /* If output should be encoded */
24+
#define NCURIPATH 1
25+
#define NCURIPWD 2
26+
#define NCURIQUERY 4
27+
#define NCURIFRAG 8
28+
#define NCURIENCODE 16 /* If output should be encoded */
2929
#define NCURIBASE (NCURIPWD|NCURIPATH)
3030
#define NCURISVC (NCURIQUERY|NCURIBASE) /* for sending to server */
3131
#define NCURIALL (NCURIPATH|NCURIPWD|NCURIQUERY|NCURIFRAG) /* for rebuilding after changes */
@@ -81,9 +81,11 @@ extern const char* ncurilookup(NCURI*, const char* param);
8181
extern const char* ncuriquerylookup(NCURI*, const char* param);
8282

8383
/* URL Encode/Decode */
84-
extern char* ncuriencode(char* s, char* allowable);
8584
extern char* ncuridecode(char* s);
86-
extern char* ncuridecodeonly(char* s, char*);
85+
/* Encode using specified character set */
86+
extern char* ncuriencodeonly(char* s, char* allowable);
87+
/* Encode user or pwd */
88+
extern char* ncuriencodeuserpwd(char* s);
8789

8890
#if defined(_CPLUSPLUS_) || defined(__CPLUSPLUS__) || defined(__CPLUSPLUS)
8991
}

libdap4/d4curlfunctions.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ set_curlflag(NCD4INFO* state, int flag)
5656
{
5757
int ret = NC_NOERR;
5858
switch (flag) {
59-
case CURLOPT_USERPWD:
60-
if(state->curl->creds.userpwd != NULL) {
61-
CHECK(state, CURLOPT_USERPWD, state->curl->creds.userpwd);
59+
case CURLOPT_USERPWD: /* Do both user and pwd */
60+
if(state->curl->creds.user != NULL
61+
&& state->curl->creds.pwd != NULL) {
62+
CHECK(state, CURLOPT_USERNAME, state->curl->creds.user);
63+
CHECK(state, CURLOPT_PASSWORD, state->curl->creds.pwd);
6264
CHECK(state, CURLOPT_HTTPAUTH, (OPTARG)CURLAUTH_ANY);
6365
}
6466
break;
@@ -107,8 +109,10 @@ set_curlflag(NCD4INFO* state, int flag)
107109
if(state->curl->proxy.host != NULL) {
108110
CHECK(state, CURLOPT_PROXY, state->curl->proxy.host);
109111
CHECK(state, CURLOPT_PROXYPORT, (OPTARG)(long)state->curl->proxy.port);
110-
if(state->curl->proxy.userpwd) {
111-
CHECK(state, CURLOPT_PROXYUSERPWD, state->curl->proxy.userpwd);
112+
if(state->curl->proxy.user != NULL
113+
&& state->curl->proxy.pwd != NULL) {
114+
CHECK(state, CURLOPT_PROXYUSERNAME, state->curl->proxy.user);
115+
CHECK(state, CURLOPT_PROXYPASSWORD, state->curl->proxy.pwd);
112116
#ifdef CURLOPT_PROXYAUTH
113117
CHECK(state, CURLOPT_PROXYAUTH, (long)CURLAUTH_ANY);
114118
#endif
@@ -264,6 +268,7 @@ NCD4_curl_protocols(NCD4globalstate* state)
264268
}
265269

266270

271+
#if 0
267272
/*
268273
"Inverse" of set_curlflag;
269274
Given a flag and value, it updates state.
@@ -349,6 +354,7 @@ NCD4_set_curlstate(NCD4INFO* state, int flag, void* value)
349354
done:
350355
return THROW(ret);
351356
}
357+
#endif
352358

353359
void
354360
NCD4_curl_printerror(NCD4INFO* state)

libdap4/d4curlfunctions.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ extern ncerror NCD4_set_flags_perfetch(NCD4INFO*);
2020
extern ncerror NCD4_set_flags_perlink(NCD4INFO*);
2121

2222
extern ncerror NCD4_set_curlflag(NCD4INFO*,int);
23-
extern ncerror NCD4_set_curlstate(NCD4INFO* state, int flag, void* value);
2423

2524
extern void NCD4_curl_debug(NCD4INFO* state);
2625

libdap4/d4file.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,10 @@ freeCurl(NCD4curl* curl)
327327
nullfree(curl->ssl.cainfo);
328328
nullfree(curl->ssl.capath);
329329
nullfree(curl->proxy.host);
330-
nullfree(curl->proxy.userpwd);
331-
nullfree(curl->creds.userpwd);
330+
nullfree(curl->proxy.user);
331+
nullfree(curl->proxy.pwd);
332+
nullfree(curl->creds.user);
333+
nullfree(curl->creds.pwd);
332334
if(curl->curlflags.createdflags & COOKIECREATED)
333335
d4removecookies(curl->curlflags.cookiejar);
334336
nullfree(curl->curlflags.cookiejar);

0 commit comments

Comments
 (0)