Skip to content

Commit

Permalink
Add support for API Key authentication (#313)
Browse files Browse the repository at this point in the history
This adds support for authenticating using API Keys.

The DSN editor has been updated to take a new "API Key" field. This
field is enabled iif no Username and Password are provided.
The field is stored in the DSN string and then used in every request for
respective configured connection.
  • Loading branch information
bpintea authored Oct 24, 2022
1 parent 0d8b656 commit 3aaf735
Show file tree
Hide file tree
Showing 9 changed files with 423 additions and 273 deletions.
96 changes: 87 additions & 9 deletions driver/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
#define HTTP_CONTENT_TYPE_JSON "Content-Type: " HTTP_APP_JSON \
"; charset=utf-8"

/* HTTP header API Key authentication-specific */
#define HTTP_AUTH_API_KEY "Authorization: ApiKey "
#define APIKEY_BUFF_SIZE 512

/* Elasticsearch/SQL data types */
/* 2 */
#define TYPE_IP "IP"
Expand Down Expand Up @@ -382,6 +386,10 @@ static void cleanup_curl(esodbc_dbc_st *dbc)
return;
}
DBGH(dbc, "libcurl: handle 0x%p cleanup.", dbc->curl);
if (dbc->curl_hdrs) {
curl_slist_free_all(dbc->curl_hdrs);
dbc->curl_hdrs = NULL;
}
dbc->curl_err = CURLE_OK;
dbc->curl_err_buff[0] = '\0';

Expand Down Expand Up @@ -442,11 +450,34 @@ SQLRETURN dbc_curl_set_url(esodbc_dbc_st *dbc, int url_type)
return SQL_ERROR;
}

/* copy of unexported Curl_slist_duplicate() libcurl function */
static struct curl_slist *curl_slist_duplicate(struct curl_slist *inlist)
{
struct curl_slist *outlist = NULL;
struct curl_slist *tmp;

while(inlist) {
tmp = curl_slist_append(outlist, inlist->data);

if(!tmp) {
curl_slist_free_all(outlist);
return NULL;
}

outlist = tmp;
inlist = inlist->next;
}
return outlist;
}

static SQLRETURN dbc_curl_init(esodbc_dbc_st *dbc)
{
CURL *curl;
SQLRETURN ret;
BOOL compress;
char apikey_buff[APIKEY_BUFF_SIZE], *apikey_ptr = NULL;
struct curl_slist *curl_hdrs;


assert(! dbc->curl);

Expand All @@ -467,14 +498,6 @@ static SQLRETURN dbc_curl_init(esodbc_dbc_st *dbc)
goto err;
}

/* set the HTTP headers: Content-Type, Accept */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_HTTPHEADER,
dbc->pack_json ? json_headers : cbor_headers);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set HTTP headers list.");
goto err;
}

/* set the behavior for redirection */
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,
dbc->follow);
Expand Down Expand Up @@ -582,8 +605,55 @@ static SQLRETURN dbc_curl_init(esodbc_dbc_st *dbc)
goto err;
}
}
} else if (dbc->api_key.cnt) {
if (sizeof(HTTP_AUTH_API_KEY) + dbc->api_key.cnt <
sizeof(apikey_buff)) {
apikey_ptr = apikey_buff;
} else {
DBGH(dbc, "static buffer size %zuB less than required %zuB, "
"allocating.", sizeof(apikey_buff), sizeof(HTTP_AUTH_API_KEY) +
dbc->api_key.cnt);
apikey_ptr = malloc(sizeof(HTTP_AUTH_API_KEY) + dbc->api_key.cnt);
if (! apikey_ptr) {
ERRNH(dbc, "OOM for %zuB.", sizeof(HTTP_AUTH_API_KEY) +
dbc->api_key.cnt);
goto err;
}
}
memcpy(apikey_ptr, HTTP_AUTH_API_KEY, sizeof(HTTP_AUTH_API_KEY) - 1);
memcpy(apikey_ptr + sizeof(HTTP_AUTH_API_KEY) - 1, dbc->api_key.str,
dbc->api_key.cnt);
apikey_ptr[sizeof(HTTP_AUTH_API_KEY) - 1 + dbc->api_key.cnt] = '\0';

dbc->curl_hdrs = curl_slist_append(NULL, apikey_ptr);
if (apikey_ptr != apikey_buff) {
free(apikey_ptr);
apikey_ptr = NULL;
}
if (! dbc->curl_hdrs) {
ERRH(dbc, "libcurl: failed to init API key Auth header.");
goto err;
}
} else {
INFOH(dbc, "no username provided: auth disabled.");
INFOH(dbc, "no username or API key provided: auth disabled.");
}

/* set the HTTP headers: Content-Type, Accept, Authorization(?) */
curl_hdrs = dbc->pack_json ? json_headers : cbor_headers;
/* is there already an Autorization header set? then chain the pack hdrs */
if (dbc->curl_hdrs) {
if (! (curl_hdrs = curl_slist_duplicate(curl_hdrs))) {
ERRNH(dbc, "failed duplicating packing format headers.");
goto err;
}
dbc->curl_hdrs->next = curl_hdrs;
/* if there's no Authz header, the pack headers won't be dup'd */
curl_hdrs = dbc->curl_hdrs;
}
dbc->curl_err = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_hdrs);
if (dbc->curl_err != CURLE_OK) {
ERRH(dbc, "libcurl: failed to set HTTP headers list.");
goto err;
}

/* proxy parameters */
Expand Down Expand Up @@ -1322,6 +1392,14 @@ SQLRETURN config_dbc(esodbc_dbc_st *dbc, esodbc_dsn_attrs_st *attrs)
/* indicates the presence of a non-empty password */
INFOH(dbc, "connection PWD: " ESODBC_PWD_VAL_SUBST ".");
}
} else if (attrs->api_key.cnt) {
if (! wstr_to_utf8(&attrs->api_key, &dbc->api_key)) {
ERRH(dbc, "failed to convert API key [%zu] `" LWPDL "` to UTF8.",
attrs->api_key.cnt, LWSTR(&attrs->api_key));
SET_HDIAG(dbc, SQL_STATE_HY000, "API key UTF8 conversion failed",
0);
goto err;
}
}

/* "follow location" param for liburl */
Expand Down
10 changes: 9 additions & 1 deletion driver/dsn.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ void TEST_API init_dsn_attrs(esodbc_dsn_attrs_st *attrs)
static inline wstr_st *mask_pwd(wstr_st *attr, wstr_st *val)
{
static wstr_st subst = WSTR_INIT(ESODBC_PWD_VAL_SUBST);
return EQ_CASE_WSTR(attr, &MK_WSTR(ESODBC_DSN_PWD)) ? &subst : val;
return EQ_CASE_WSTR(attr, &MK_WSTR(ESODBC_DSN_PWD)) ||
EQ_CASE_WSTR(attr, &MK_WSTR(ESODBC_DSN_API_KEY)) ? &subst : val;
}

#define DSN_NOT_MATCHED 0
Expand Down Expand Up @@ -61,6 +62,7 @@ int assign_dsn_attr(esodbc_dsn_attrs_st *attrs,
{&MK_WSTR(ESODBC_DSN_DSN), &attrs->dsn},
{&MK_WSTR(ESODBC_DSN_PWD), &attrs->pwd},
{&MK_WSTR(ESODBC_DSN_UID), &attrs->uid},
{&MK_WSTR(ESODBC_DSN_API_KEY), &attrs->api_key},
{&MK_WSTR(ESODBC_DSN_SAVEFILE), &attrs->savefile},
{&MK_WSTR(ESODBC_DSN_FILEDSN), &attrs->filedsn},
{&MK_WSTR(ESODBC_DSN_CLOUD_ID), &attrs->cloud_id},
Expand Down Expand Up @@ -404,6 +406,7 @@ long TEST_API write_00_list(esodbc_dsn_attrs_st *attrs,
{&MK_WSTR(ESODBC_DSN_DSN), &attrs->dsn},
{&MK_WSTR(ESODBC_DSN_PWD), &attrs->pwd},
{&MK_WSTR(ESODBC_DSN_UID), &attrs->uid},
{&MK_WSTR(ESODBC_DSN_API_KEY), &attrs->api_key},
{&MK_WSTR(ESODBC_DSN_SAVEFILE), &attrs->savefile},
{&MK_WSTR(ESODBC_DSN_FILEDSN), &attrs->filedsn},
{&MK_WSTR(ESODBC_DSN_CLOUD_ID), &attrs->cloud_id},
Expand Down Expand Up @@ -637,6 +640,10 @@ BOOL write_system_dsn(esodbc_dsn_attrs_st *new_attrs,
&MK_WSTR(ESODBC_DSN_UID), &new_attrs->uid,
old_attrs ? &old_attrs->uid : NULL
},
{
&MK_WSTR(ESODBC_DSN_API_KEY), &new_attrs->api_key,
old_attrs ? &old_attrs->api_key : NULL
},
/* SAVEILE */
/* FILEDSN */
{
Expand Down Expand Up @@ -819,6 +826,7 @@ long TEST_API write_connection_string(esodbc_dsn_attrs_st *attrs,
{&attrs->dsn, &MK_WSTR(ESODBC_DSN_DSN)},
{&attrs->pwd, &MK_WSTR(ESODBC_DSN_PWD)},
{&attrs->uid, &MK_WSTR(ESODBC_DSN_UID)},
{&attrs->api_key, &MK_WSTR(ESODBC_DSN_API_KEY)},
{&attrs->savefile, &MK_WSTR(ESODBC_DSN_SAVEFILE)},
{&attrs->filedsn, &MK_WSTR(ESODBC_DSN_FILEDSN)},
{&attrs->cloud_id, &MK_WSTR(ESODBC_DSN_CLOUD_ID)},
Expand Down
4 changes: 3 additions & 1 deletion driver/dsn.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#define ESODBC_DSN_DSN "DSN"
#define ESODBC_DSN_PWD "PWD"
#define ESODBC_DSN_UID "UID"
#define ESODBC_DSN_API_KEY "APIKey"
#define ESODBC_DSN_SAVEFILE "SAVEFILE"
#define ESODBC_DSN_FILEDSN "FILEDSN"
#define ESODBC_DSN_CLOUD_ID "CloudID"
Expand Down Expand Up @@ -71,6 +72,7 @@ typedef struct {
wstr_st dsn;
wstr_st pwd;
wstr_st uid;
wstr_st api_key;
wstr_st savefile;
wstr_st filedsn;
wstr_st cloud_id;
Expand Down Expand Up @@ -102,7 +104,7 @@ typedef struct {
wstr_st trace_enabled;
wstr_st trace_file;
wstr_st trace_level;
#define ESODBC_DSN_ATTRS_COUNT 36
#define ESODBC_DSN_ATTRS_COUNT 37

SQLWCHAR buff[ESODBC_DSN_ATTRS_COUNT * ESODBC_DSN_MAX_ATTR_LEN];
/* DSN reading/writing functions are passed a SQLSMALLINT length param */
Expand Down
6 changes: 5 additions & 1 deletion driver/handles.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ typedef struct struct_dbc {
cstr_st ca_path;

cstr_st uid;
cstr_st pwd;
union {
cstr_st pwd; /* when dbc configuring, pwd is only set if uid is */
cstr_st api_key;
};
SQLUINTEGER timeout;
BOOL follow;
struct {
Expand Down Expand Up @@ -204,6 +207,7 @@ typedef struct struct_dbc {
size_t apos; /* current write position in the abuff */
size_t amax; /* maximum length (bytes) that abuff can grow to */
esodbc_mutex_lt curl_mux; /* mutex for above 'networking' members */
struct curl_slist *curl_hdrs; /* HTTP headers list */

/* window handler */
HWND hwin;
Expand Down
Loading

0 comments on commit 3aaf735

Please sign in to comment.