netcdf-c/libdispatch/dauth.c
Dennis Heimbigner 27f615bebc Properly handle missing regions in URLS
NOTE: it is important that this fix gets into 4.9.3

re: Issue https://github.com/Unidata/netcdf-c/issues/2798

## Modifications
* This PR includes PR https://github.com/Unidata/netcdf-c/pull/2813
* Support the following AWS environment variables in the internal S3 library
  (they are already supported by aws-sdk-cpp).
  - AWS_REGION
  - AWS_DEFAULT_REGION
  - AWS_ACCESS_KEY_ID
  - AWS_CONFIG_FILE
  - AWS_PROFILE
  - AWS_SECRET_ACCESS_KEY
  - (source https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html).
* Support an empty region when specifying s3.amazonaws.com as the host.
* Move some S3/AWS related functions to ds3util.c
* Add a test case to test empty region and AWS_[DEFAULT]_REGION.
2023-12-02 21:03:59 -07:00

420 lines
12 KiB
C

/*
Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
See COPYRIGHT for license information.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#include "netcdf.h"
#include "ncbytes.h"
#include "ncuri.h"
#include "nclog.h"
#include "ncpathmgr.h"
#include "ncs3sdk.h"
#include "ncauth.h"
#ifdef _MSC_VER
#include <windows.h>
#endif
#include "ncrc.h"
#undef DEBUG
#undef MEMCHECK
#define MEMCHECK(x) if((x)==NULL) {goto nomem;} else {}
/* Define the curl flag defaults in envv style */
static const char* AUTHDEFAULTS[] = {
"HTTP.SSL.VERIFYPEER","-1", /* Use default */
"HTTP.SSL.VERIFYHOST","-1", /* Use default */
"HTTP.TIMEOUT","1800", /*seconds */ /* Long but not infinite */
"HTTP.CONNECTTIMEOUT","50", /*seconds */ /* Long but not infinite */
"HTTP.ENCODE","1", /* Use default */
NULL,
};
/* Forward */
static int setauthfield(NCauth* auth, const char* flag, const char* value);
static void setdefaults(NCauth*);
/**************************************************/
/* External Entry Points */
int
NC_parseproxy(NCauth* auth, const char* surl)
{
int ret = NC_NOERR;
NCURI* uri = NULL;
if(surl == NULL || strlen(surl) == 0)
return (NC_NOERR); /* nothing there*/
if(ncuriparse(surl,&uri))
return (NC_EURL);
auth->proxy.user = uri->user;
auth->proxy.pwd = uri->password;
auth->proxy.host = strdup(uri->host);
if(uri->port != NULL)
auth->proxy.port = atoi(uri->port);
else
auth->proxy.port = 80;
return (ret);
}
char*
NC_combinehostport(NCURI* uri)
{
size_t len;
char* host = NULL;
char* port = NULL;
char* hp = NULL;
if(uri == NULL) return NULL;
host = uri->host;
port = uri->port;
if(uri == NULL || host == NULL) return NULL;
if(port != NULL && strlen(port) == 0) port = NULL;
len = strlen(host);
if(port != NULL) len += (1+strlen(port));
hp = (char*)malloc(len+1);
if(hp == NULL) return NULL;
snprintf(hp, len+1, "%s%s%s", host, port ? ":" : "", port ? port : "");
return hp;
}
int
NC_authsetup(NCauth** authp, NCURI* uri)
{
int ret = NC_NOERR;
char* uri_hostport = NULL;
NCauth* auth = NULL;
struct AWSprofile* ap = NULL;
if(uri != NULL)
uri_hostport = NC_combinehostport(uri);
else
{ret = NC_EDAP; goto done;} /* Generic EDAP error. */
if((auth=calloc(1,sizeof(NCauth)))==NULL)
{ret = NC_ENOMEM; goto done;}
setdefaults(auth);
/* Note, we still must do this function even if
ncrc_getglobalstate()->rc.ignore is set in order
to getinfo e.g. host+port from url
*/
setauthfield(auth,"HTTP.VERBOSE",
NC_rclookup("HTTP.VERBOSE",uri_hostport,uri->path));
setauthfield(auth,"HTTP.TIMEOUT",
NC_rclookup("HTTP.TIMEOUT",uri_hostport,uri->path));
setauthfield(auth,"HTTP.CONNECTTIMEOUT",
NC_rclookup("HTTP.CONNECTTIMEOUT",uri_hostport,uri->path));
setauthfield(auth,"HTTP.USERAGENT",
NC_rclookup("HTTP.USERAGENT",uri_hostport,uri->path));
setauthfield(auth,"HTTP.COOKIEFILE",
NC_rclookup("HTTP.COOKIEFILE",uri_hostport,uri->path));
setauthfield(auth,"HTTP.COOKIE_FILE",
NC_rclookup("HTTP.COOKIE_FILE",uri_hostport,uri->path));
setauthfield(auth,"HTTP.COOKIEJAR",
NC_rclookup("HTTP.COOKIEJAR",uri_hostport,uri->path));
setauthfield(auth,"HTTP.COOKIE_JAR",
NC_rclookup("HTTP.COOKIE_JAR",uri_hostport,uri->path));
setauthfield(auth,"HTTP.PROXY.SERVER",
NC_rclookup("HTTP.PROXY.SERVER",uri_hostport,uri->path));
setauthfield(auth,"HTTP.PROXY_SERVER",
NC_rclookup("HTTP.PROXY_SERVER",uri_hostport,uri->path));
setauthfield(auth,"HTTP.SSL.CERTIFICATE",
NC_rclookup("HTTP.SSL.CERTIFICATE",uri_hostport,uri->path));
setauthfield(auth,"HTTP.SSL.KEY",
NC_rclookup("HTTP.SSL.KEY",uri_hostport,uri->path));
setauthfield(auth,"HTTP.SSL.KEYPASSWORD",
NC_rclookup("HTTP.SSL.KEYPASSWORD",uri_hostport,uri->path));
setauthfield(auth,"HTTP.SSL.CAINFO",
NC_rclookup("HTTP.SSL.CAINFO",uri_hostport,uri->path));
setauthfield(auth,"HTTP.SSL.CAPATH",
NC_rclookup("HTTP.SSL.CAPATH",uri_hostport,uri->path));
setauthfield(auth,"HTTP.SSL.VERIFYPEER",
NC_rclookup("HTTP.SSL.VERIFYPEER",uri_hostport,uri->path));
setauthfield(auth,"HTTP.SSL.VERIFYHOST",
NC_rclookup("HTTP.SSL.VERIFYHOST",uri_hostport,uri->path));
/* Alias for VERIFYHOST + VERIFYPEER */
setauthfield(auth,"HTTP.SSL.VALIDATE",
NC_rclookup("HTTP.SSL.VALIDATE",uri_hostport,uri->path));
setauthfield(auth,"HTTP.NETRC",
NC_rclookup("HTTP.NETRC",uri_hostport,uri->path));
{ /* Handle various cases for user + password */
/* First, see if the user+pwd was in the original url */
char* user = NULL;
char* pwd = NULL;
if(uri->user != NULL && uri->password != NULL) {
user = uri->user;
pwd = uri->password;
} else {
user = NC_rclookup("HTTP.CREDENTIALS.USER",uri_hostport,uri->path);
pwd = NC_rclookup("HTTP.CREDENTIALS.PASSWORD",uri_hostport,uri->path);
}
if(user != NULL && pwd != NULL) {
user = strdup(user); /* so we can consistently reclaim */
pwd = strdup(pwd);
} else {
/* Could not get user and pwd, so try USERPASSWORD */
const char* userpwd = NC_rclookup("HTTP.CREDENTIALS.USERPASSWORD",uri_hostport,uri->path);
if(userpwd != NULL) {
if((ret = NC_parsecredentials(userpwd,&user,&pwd))) goto done;
}
}
setauthfield(auth,"HTTP.CREDENTIALS.USERNAME",user);
setauthfield(auth,"HTTP.CREDENTIALS.PASSWORD",pwd);
nullfree(user);
nullfree(pwd);
}
/* Get the Default profile */
if((ret=NC_authgets3profile("no",&ap))) goto done;
if(ap == NULL)
if((ret=NC_authgets3profile("default",&ap))) goto done;
if(ap != NULL)
auth->s3profile = strdup(ap->name);
else
auth->s3profile = NULL;
if(authp) {*authp = auth; auth = NULL;}
done:
nullfree(uri_hostport);
return (ret);
}
void
NC_authfree(NCauth* auth)
{
if(auth == NULL) return;
if(auth->curlflags.cookiejarcreated) {
#ifdef _MSC_VER
DeleteFile(auth->curlflags.cookiejar);
#else
remove(auth->curlflags.cookiejar);
#endif
}
nullfree(auth->curlflags.useragent);
nullfree(auth->curlflags.cookiejar);
nullfree(auth->curlflags.netrc);
nullfree(auth->ssl.certificate);
nullfree(auth->ssl.key);
nullfree(auth->ssl.keypasswd);
nullfree(auth->ssl.cainfo);
nullfree(auth->ssl.capath);
nullfree(auth->proxy.host);
nullfree(auth->proxy.user);
nullfree(auth->proxy.pwd);
nullfree(auth->creds.user);
nullfree(auth->creds.pwd);
nullfree(auth->s3profile);
nullfree(auth);
}
/**************************************************/
static int
setauthfield(NCauth* auth, const char* flag, const char* value)
{
int ret = NC_NOERR;
if(value == NULL) goto done;
if(strcmp(flag,"HTTP.ENCODE")==0) {
if(atoi(value)) {auth->curlflags.encode = 1;} else {auth->curlflags.encode = 0;}
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.encode: %ld", (long)auth->curlflags.encode);
#endif
}
if(strcmp(flag,"HTTP.VERBOSE")==0) {
if(atoi(value)) auth->curlflags.verbose = 1;
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.VERBOSE: %ld", (long)auth->curlflags.verbose);
#endif
}
if(strcmp(flag,"HTTP.TIMEOUT")==0) {
if(atoi(value)) auth->curlflags.timeout = atoi(value);
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.TIMEOUT: %ld", (long)auth->curlflags.timeout);
#endif
}
if(strcmp(flag,"HTTP.CONNECTTIMEOUT")==0) {
if(atoi(value)) auth->curlflags.connecttimeout = atoi(value);
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.CONNECTTIMEOUT: %ld", (long)auth->curlflags.connecttimeout);
#endif
}
if(strcmp(flag,"HTTP.USERAGENT")==0) {
if(atoi(value)) auth->curlflags.useragent = strdup(value);
MEMCHECK(auth->curlflags.useragent);
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.USERAGENT: %s", auth->curlflags.useragent);
#endif
}
if(
strcmp(flag,"HTTP.COOKIEFILE")==0
|| strcmp(flag,"HTTP.COOKIE_FILE")==0
|| strcmp(flag,"HTTP.COOKIEJAR")==0
|| strcmp(flag,"HTTP.COOKIE_JAR")==0
) {
nullfree(auth->curlflags.cookiejar);
auth->curlflags.cookiejar = strdup(value);
MEMCHECK(auth->curlflags.cookiejar);
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.COOKIEJAR: %s", auth->curlflags.cookiejar);
#endif
}
if(strcmp(flag,"HTTP.PROXY.SERVER")==0 || strcmp(flag,"HTTP.PROXY_SERVER")==0) {
ret = NC_parseproxy(auth,value);
if(ret != NC_NOERR) goto done;
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.PROXY.SERVER: %s", value);
#endif
}
if(strcmp(flag,"HTTP.SSL.VERIFYPEER")==0) {
int v;
if((v = atol(value))) {
auth->ssl.verifypeer = v;
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.SSL.VERIFYPEER: %d", v);
#endif
}
}
if(strcmp(flag,"HTTP.SSL.VERIFYHOST")==0) {
int v;
if((v = atol(value))) {
auth->ssl.verifyhost = v;
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.SSL.VERIFYHOST: %d", v);
#endif
}
}
if(strcmp(flag,"HTTP.SSL.VALIDATE")==0) {
if(atoi(value)) {
auth->ssl.verifypeer = 1;
auth->ssl.verifyhost = 2;
}
}
if(strcmp(flag,"HTTP.SSL.CERTIFICATE")==0) {
nullfree(auth->ssl.certificate);
auth->ssl.certificate = strdup(value);
MEMCHECK(auth->ssl.certificate);
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.SSL.CERTIFICATE: %s", auth->ssl.certificate);
#endif
}
if(strcmp(flag,"HTTP.SSL.KEY")==0) {
nullfree(auth->ssl.key);
auth->ssl.key = strdup(value);
MEMCHECK(auth->ssl.key);
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.SSL.KEY: %s", auth->ssl.key);
#endif
}
if(strcmp(flag,"HTTP.SSL.KEYPASSWORD")==0) {
nullfree(auth->ssl.keypasswd) ;
auth->ssl.keypasswd = strdup(value);
MEMCHECK(auth->ssl.keypasswd);
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.SSL.KEYPASSWORD: %s", auth->ssl.keypasswd);
#endif
}
if(strcmp(flag,"HTTP.SSL.CAINFO")==0) {
nullfree(auth->ssl.cainfo) ;
auth->ssl.cainfo = strdup(value);
MEMCHECK(auth->ssl.cainfo);
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.SSL.CAINFO: %s", auth->ssl.cainfo);
#endif
}
if(strcmp(flag,"HTTP.SSL.CAPATH")==0) {
nullfree(auth->ssl.capath) ;
auth->ssl.capath = strdup(value);
MEMCHECK(auth->ssl.capath);
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.SSL.CAPATH: %s", auth->ssl.capath);
#endif
}
if(strcmp(flag,"HTTP.NETRC")==0) {
nullfree(auth->curlflags.netrc);
auth->curlflags.netrc = strdup(value);
MEMCHECK(auth->curlflags.netrc);
#ifdef DEBUG
nclog(NCLOGNOTE,"HTTP.NETRC: %s", auth->curlflags.netrc);
#endif
}
if(strcmp(flag,"HTTP.CREDENTIALS.USERNAME")==0) {
nullfree(auth->creds.user);
auth->creds.user = strdup(value);
MEMCHECK(auth->creds.user);
}
if(strcmp(flag,"HTTP.CREDENTIALS.PASSWORD")==0) {
nullfree(auth->creds.pwd);
auth->creds.pwd = strdup(value);
MEMCHECK(auth->creds.pwd);
}
done:
return (ret);
nomem:
return (NC_ENOMEM);
}
/*
Given form user:pwd, parse into user and pwd
and do %xx unescaping
*/
int
NC_parsecredentials(const char* userpwd, char** userp, char** pwdp)
{
char* user = NULL;
char* pwd = NULL;
if(userpwd == NULL)
return NC_EINVAL;
user = strdup(userpwd);
if(user == NULL)
return NC_ENOMEM;
pwd = strchr(user,':');
if(pwd == NULL) {
free(user);
return NC_EINVAL;
}
*pwd = '\0';
pwd++;
if(userp)
*userp = ncuridecode(user);
if(pwdp)
*pwdp = ncuridecode(pwd);
free(user);
return NC_NOERR;
}
static void
setdefaults(NCauth* auth)
{
int ret = NC_NOERR;
const char** p;
for(p=AUTHDEFAULTS;*p;p+=2) {
ret = setauthfield(auth,p[0],p[1]);
if(ret) {
nclog(NCLOGERR, "RC file defaulting failed for: %s=%s",p[0],p[1]);
}
}
}