Skip to content

Commit 8416b11

Browse files
authored
Merge pull request #472 from Unidata/netrc.dmh
Fix bug in ocrc.c#combinecredentials
2 parents 8b824a3 + 67018ec commit 8416b11

File tree

17 files changed

+431
-204
lines changed

17 files changed

+431
-204
lines changed

docs/auth.md

Lines changed: 118 additions & 61 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}
@@ -37,29 +37,92 @@ directly insert the username and the password into a url in this form.
3737
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.
40+
4041
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
42+
authorization may work with this but it is a security risk.
43+
This is because the username and password
44+
may be sent to each server in the redirection chain.
45+
46+
Note also that the `user:password` form may contain characters that must be
47+
escaped. See the <a href="#USERPWDESCAPE">password escaping</a> section to see
48+
how to properly escape the user and password.
4349

4450
## RC File Authentication {#DODSRC}
4551
The netcdf library supports an _rc_ file mechanism to allow the passing
4652
of a number of parameters to libnetcdf and libcurl.
53+
Locating the _rc_ file is a multi-step process.
54+
55+
### Search Order
4756

4857
The file must be called one of the following names:
4958
".daprc" or ".dodsrc".
5059
If both ".daprc" and ".dodsrc" exist, then
5160
the ".daprc" file will take precedence.
5261

53-
The rc file is searched for first in the current directory
54-
and then in the home directory (as defined by the HOME environment
55-
variable).
62+
It is strongly suggested that you pick one of the two names
63+
and use it always. Otherwise you may observe unexpected results
64+
when the netcdf-c library finds one that you did not intend.
65+
66+
The search for an _rc_ file looks in the following places in this order.
67+
68+
1. Check for the environment variable named _DAPRCFILE_.
69+
This will specify the full path for the _rc_ file
70+
(not just the containing directory).
71+
2. Search the current working directory (`./`) looking
72+
for (in order) .daprc or .dodsrc.
73+
3. Search the HOME directory (`$HOME`) looking
74+
for (in order) .daprc or .dodsrc. The HOME environment
75+
variable is used to define the directory in which to search.
76+
77+
It is strongly suggested that you pick a uniform location
78+
and use it always. Otherwise you may observe unexpected results
79+
when the netcdf-c library get an rc file you did not expect.
80+
81+
### RC File Format
5682

5783
The rc file format is a series of lines of the general form:
5884

5985
[<host:port>]<key>=<value>
6086

61-
where the bracket-enclosed host:port is optional and will be discussed
62-
subsequently.
87+
where the bracket-enclosed host:port is optional.
88+
89+
### URL Constrained RC File Entries
90+
91+
Each line of the rc file can begin with
92+
a host+port enclosed in square brackets.
93+
The form is "host:port".
94+
If the port is not specified
95+
then the form is just "host".
96+
The reason that more of the url is not used is that
97+
libcurl's authorization grain is not any finer than host level.
98+
99+
Examples.
100+
101+
[remotetest.unidata.ucar.edu]HTTP.VERBOSE=1
102+
103+
or
104+
105+
[fake.ucar.edu:9090]HTTP.VERBOSE=0
106+
107+
If the url request from, say, the _netcdf_open_ method
108+
has a host+port matching one of the prefixes in the rc file, then
109+
the corresponding entry will be used, otherwise ignored.
110+
This means that an entry with a matching host+port will take
111+
precedence over an entry without a host+port.
112+
113+
For example, the URL
114+
115+
http://remotetest.unidata.ucar.edu/thredds/dodsC/testdata/testData.nc
116+
117+
will have HTTP.VERBOSE set to 1 because its host matches the example above.
118+
119+
Similarly,
120+
121+
http://fake.ucar.edu:9090/dts/test.01
122+
123+
will have HTTP.VERBOSE set to 0 because its host+port matches the example above.
124+
125+
## Authorization-Related Keys {#AUTHKEYS}
63126

64127
The currently defined set of authorization-related keys are as follows.
65128
The second column is the affected curl_easy_setopt option(s), if any.
@@ -71,12 +134,12 @@ The second column is the affected curl_easy_setopt option(s), if any.
71134
<tr><td>HTTP.SSL.CERTIFICATE</td><td>CURLOPT_SSLCERT</td>
72135
<tr><td>HTTP.SSL.KEY</td><td>CURLOPT_SSLKEY</td>
73136
<tr><td>HTTP.SSL.KEYPASSWORD</td><td>CURLOPT_KEYPASSWORD</td>
74-
<tr><td>HTTP.SSL.CAINFO</td><td>CURLOPT_SSLCAINFO</td>
75-
<tr><td>HTTP.SSL.CAPATH</td><td>CURLOPT_SSLCAPATH</td>
137+
<tr><td>HTTP.SSL.CAINFO</td><td>CURLOPT_CAINFO</td>
138+
<tr><td>HTTP.SSL.CAPATH</td><td>CURLOPT_CAPATH</td>
76139
<tr><td>HTTP.SSL.VERIFYPEER</td><td>CURLOPT_SSL_VERIFYPEER</td>
77140
<tr><td>HTTP.SSL.VALIDATE</td><td>CURLOPT_SSL_VERIFYPEER, CURLOPT_SSL_VERIFYHOST</td>
78141
<tr><td>HTTP.CREDENTIALS.USERPASSWORD</td><td>CURLOPT_USERPASSWORD</td>
79-
<tr><td>HTTP.NETRC</td><td>N.A.</td><td>Specify path of the .netrc file</td>
142+
<tr><td>HTTP.NETRC</td><td>CURLOPT_NETRC,CURLOPT_NETRC_FILE</td>
80143
</table>
81144

82145
### Password Authentication
@@ -86,7 +149,9 @@ HTTP.CREDENTIALS.USERPASSWORD
86149
can be used to set the simple password authentication.
87150
This is an alternative to setting it in the url.
88151
The value must be of the form "username:password".
89-
See <a href="#REDIR">redirection authorization</a>
152+
See the <a href="#USERPWDESCAPE">password escaping</a> section
153+
to see how this value must escape certain characters.
154+
Also see <a href="#REDIR">redirection authorization</a>
90155
for important additional information.
91156

92157
### Cookie Jar
@@ -129,6 +194,29 @@ specifies the absolute path of the .netrc file.
129194
See [redirection authorization](#REDIR)
130195
for information about using .netrc.
131196

197+
## Password Escaping {#USERPWDESCAPE}
198+
199+
With current password rules, it is is not unlikely that the password
200+
will contain characters that need to be escaped. Similarly, the user
201+
may contain characters such as '@' that need to be escaped. To support this,
202+
it is assumed that all occurrences of `user:password` use URL (i.e. %%XX)
203+
escaping for at least the characters in the table below.
204+
205+
The minimum set of characters that must be escaped depends on the location.
206+
If the user+pwd is embedded in the URL, then '@' and ':' __must__ be escaped.
207+
If the user+pwd is the value for
208+
the HTTP.CREDENTIALS.USERPASSWORD key in the _rc_ file, then
209+
':' __must__ be escaped.
210+
Escaping should __not__ be used in the `.netrc` file.
211+
212+
The relevant escape codes are as follows.
213+
<table>
214+
<tr><th>Character</th><th>Escaped Form</th>
215+
<tr><td>'@'</td><td>%40</td>
216+
<tr><td>':'</td><td>%3a</td>
217+
</table>
218+
Additional characters can be escaped if desired.
219+
132220
## Redirection-Based Authentication {#REDIR}
133221

134222
Some sites provide authentication by using a third party site
@@ -145,16 +233,18 @@ using the _https_ protocol (note the use of _https_ instead of _http_).
145233
4. URS sends a redirect (with authorization information) to send
146234
the client back to the SOI to actually obtain the data.
147235

148-
It turns out that libcurl uses the password in the `.daprc`
149-
file (or from the url)
150-
only for the initial connection. This causes problems because
151-
the redirected connection is the one that actually requires the password.
152-
This is where the `.netrc` file comes in. Libcurl will use `.netrc` for
153-
the redirected connection. It is possible to cause libcurl to use
154-
the `.daprc` password always, but this introduces a security hole
155-
because it may send the initial user+pwd to the redirection site.
156-
In summary, if you are using redirection, then you must create a `.netrc`
157-
file to hold the password for the site to which the redirection is sent.
236+
It turns out that libcurl, by default, uses the password in the
237+
`.daprc` file (or from the url) for all connections that request
238+
a password. This causes problems because only the the specific
239+
redirected connection is the one that actually requires the password.
240+
This is where the `.netrc` file comes in. Libcurl will use `.netrc`
241+
for the redirected connection. It is possible to cause libcurl
242+
to use the `.daprc` password always, but this introduces a
243+
security hole because it may send the initial user+pwd to every
244+
server in the redirection chain.
245+
In summary, if you are using redirection, then you are
246+
''strongly'' encouraged to create a `.netrc` file to hold the
247+
password for the site to which the redirection is sent.
158248

159249
The format of this `.netrc` file will contain lines that
160250
typically look like this.
@@ -165,52 +255,19 @@ where the machine, mmmmmm, is the hostname of the machine to
165255
which the client is redirected for authorization, and the
166256
login and password are those needed to authenticate on that machine.
167257

168-
The `.netrc` file can be specified by
258+
The location of the `.netrc` file can be specified by
169259
putting the following line in your `.daprc`/`.dodsrc` file.
170260

171261
HTTP.NETRC=<path to netrc file>
172262

263+
If not specified, then libcurl will look first in the current
264+
directory, and then in the HOME directory.
265+
173266
One final note. In using this, you MUST
174267
to specify a real file in the file system to act as the
175268
cookie jar file (HTTP.COOKIEJAR) so that the
176269
redirect site can properly pass back authorization information.
177270

178-
## URL Constrained RC File Entries {#URLCONS}
179-
180-
Each line of the rc file can begin with
181-
a host+port enclosed in square brackets.
182-
The form is "host:port".
183-
If the port is not specified
184-
then the form is just "host".
185-
The reason that more of the url is not used is that
186-
libcurl's authorization grain is not any finer than host level.
187-
188-
Examples.
189-
190-
[remotetest.unidata.ucar.edu]HTTP.VERBOSE=1
191-
192-
or
193-
194-
[fake.ucar.edu:9090]HTTP.VERBOSE=0
195-
196-
If the url request from, say, the _netcdf_open_ method
197-
has a host+port matching one of the prefixes in the rc file, then
198-
the corresponding entry will be used, otherwise ignored.
199-
This means that an entry with a matching host+port will take
200-
precedence over an entry without a host+port.
201-
202-
For example, the URL
203-
204-
http://remotetest.unidata.ucar.edu/thredds/dodsC/testdata/testData.nc
205-
206-
will have HTTP.VERBOSE set to 1 because its host matches the example above.
207-
208-
Similarly,
209-
210-
http://fake.ucar.edu:9090/dts/test.01
211-
212-
will have HTTP.VERBOSE set to 0 because its host+port matches the example above.
213-
214271
## Client-Side Certificates {#CLIENTCERTS}
215272

216273
Some systems, notably ESG (Earth System Grid), requires
@@ -244,8 +301,8 @@ the code is definitive.
244301
<tr><td>HTTP.SSL.CERTIFICATE</td><td>CUROPT_SSLCERT</td>
245302
<tr><td>HTTP.SSL.KEY</td><td>CUROPT_SSLKEY</td>
246303
<tr><td>HTTP.SSL.KEYPASSWORD</td><td>CUROPT_KEYPASSWORD</td>
247-
<tr><td>HTTP.SSL.CAINFO</td><td>CUROPT_SSLCAINFO</td>
248-
<tr><td>HTTP.SSL.CAPATH</td><td>CUROPT_SSLCAPATH</td>
304+
<tr><td>HTTP.SSL.CAINFO</td><td>CUROPT_CAINFO</td>
305+
<tr><td>HTTP.SSL.CAPATH</td><td>CUROPT_CAPATH</td>
249306
<tr><td>HTTP.SSL.VERIFYPEER</td><td>CUROPT_SSL_VERIFYPEER</td>
250307
<tr><td>HTTP.CREDENTIALS.USERPASSWORD</td><td>CUROPT_USERPASSWORD</td>
251308
<tr><td>HTTP.NETRC</td><td>CURLOPT_NETRC,CURLOPT_NETRC_FILE</td>

include/ncuri.h

Lines changed: 11 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,13 @@ 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+
/* Partial decode */
86+
extern char* ncuridecodepartial(char* s, const char* decodeset);
87+
/* Encode using specified character set */
88+
extern char* ncuriencodeonly(char* s, char* allowable);
89+
/* Encode user or pwd */
90+
extern char* ncuriencodeuserpwd(char* s);
8791

8892
#if defined(_CPLUSPLUS_) || defined(__CPLUSPLUS__) || defined(__CPLUSPLUS)
8993
}

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
@@ -28,7 +28,6 @@ extern ncerror NCD4_set_flags_perfetch(NCD4INFO*);
2828
extern ncerror NCD4_set_flags_perlink(NCD4INFO*);
2929

3030
extern ncerror NCD4_set_curlflag(NCD4INFO*,int);
31-
extern ncerror NCD4_set_curlstate(NCD4INFO* state, int flag, void* value);
3231

3332
extern void NCD4_curl_debug(NCD4INFO* state);
3433

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)