Skip to content
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

Auth methode #2

Merged
merged 1 commit into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 130 additions & 47 deletions lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,17 @@
#include <string.h>
#include <assert.h>
#include "curl/curl.h"
#include <time.h>
#include "osc_sdk.h"
#include "json.h"

#define AK_SIZE 20
#define SK_SIZE 40
#define TIMESTAMP_SIZE 17
#define TIME_HDR_KEY "X-Osc-Date: "
#define TIME_HDR_KEY_L (sizeof TIME_HDR_KEY)

#define CFG_FILE "/.osc/config.json"

#ifdef _WIN32

Expand Down Expand Up @@ -166,45 +172,76 @@ int osc_str_append_string(struct osc_str *osc_str, const char *str)
return 0;
}

#define LOAD_CFG_TRY(test, ...) \
if (test) fprintf(stderr, __VA_ARGS__); return -1;

#define LOAD_CFG_GET_HOME(buf) \
{ \
const char *dest = CFG_FILE; \
char *home = getenv("HOME"); \
\
LOAD_CFG_TRY(strlen(home) + sizeof CFG_FILE > sizeof buf, \
"home path too big"); \
strcpy(stpcpy(buf, home), dest); \
}


int osc_load_ak_sk_from_conf(const char *profile, char **ak, char **sk)
{
const char *dest = "/.osc/config.json";
char *home = getenv("HOME");
char buf[1024];
struct json_object *js;
struct json_object *js, *ak_js, *sk_js;

LOAD_CFG_GET_HOME(buf);
*sk = NULL;
*ak = NULL;
strcpy(stpcpy(buf, home), dest);
js = json_object_from_file(buf);
if (!js) {
fprintf(stderr, "can't open %s\n", buf);
return -1;
}
LOAD_CFG_TRY(!js, "can't open %s\n", buf);
js = json_object_object_get(js, profile);
if (!js) {
fprintf(stderr, "can't find profile '%s'\n", profile);
return -1;
}
LOAD_CFG_TRY(!js, "can't find profile %s\n", profile);
ak_js = json_object_object_get(js, "access_key");
LOAD_CFG_TRY(!ak_js, "can't find 'access_key' in profile '%s'\n", profile);
sk_js = json_object_object_get(js, "secret_key");
LOAD_CFG_TRY(!sk_js, "can't find 'secret_key' in profile '%s'\n", profile);

*ak = strdup(json_object_get_string(json_object_object_get(js, "access_key")));
*sk = strdup(json_object_get_string(json_object_object_get(js, "secret_key")));
return 0;
}

int osc_load_loging_password_from_conf(const char *profile,
char **email, char **password)
{
char buf[1024];
struct json_object *js, *login_js, *pass_js;

LOAD_CFG_GET_HOME(buf)
*password = NULL;
*email = NULL;
js = json_object_from_file(buf);
LOAD_CFG_TRY(!js, "can't open %s\n", buf);
js = json_object_object_get(js, profile);
LOAD_CFG_TRY(!js, "can't find profile '%s'\n", profile);
login_js = json_object_object_get(js, "login");
LOAD_CFG_TRY(!login_js, "can't find 'login' in profile '%s'\n", profile);
*email = strdup(json_object_get_string(login_js));

pass_js = json_object_object_get(js, "password");
if (!pass_js) {
return 0; /* is optional */
}
*password = strdup(json_object_get_string(pass_js));
return 0;
}

int osc_load_region_from_conf(const char *profile, char **region)
{
const char *dest = "/.osc/config.json";
char *home = getenv("HOME");
struct json_object *region_obj;
char buf[1024];
struct json_object *js;

strcpy(stpcpy(buf, home), dest);
LOAD_CFG_GET_HOME(buf)
js = json_object_from_file(buf);
if (!js) {
fprintf(stderr, "can't open %s\n", buf);
return -1;
}
LOAD_CFG_TRY(!js, "can't open %s\n", buf);
js = json_object_object_get(js, profile);
if (!js)
return -1;
Expand All @@ -219,18 +256,13 @@ int osc_load_region_from_conf(const char *profile, char **region)

int osc_load_cert_from_conf(const char *profile, char **cert, char **key)
{
const char *dest = "/.osc/config.json";
char *home = getenv("HOME");
struct json_object *cert_obj, *key_obj, *js;
char buf[1024];
int ret = 0;

strcpy(stpcpy(buf, home), dest);
LOAD_CFG_GET_HOME(buf)
js = json_object_from_file(buf);
if (!js) {
fprintf(stderr, "can't open %s\n", buf);
return -1;
}
LOAD_CFG_TRY(!js, "can't open %s\n", buf);
js = json_object_object_get(js, profile);
if (!js)
return 0;
Expand Down Expand Up @@ -290,34 +322,60 @@ int osc_sdk_set_useragent(struct osc_env *e, const char *str)

int osc_init_sdk(struct osc_env *e, const char *profile, unsigned int flag)
{
char ak_sk[AK_SIZE + SK_SIZE + 2];
char *ca = getenv("CURL_CA_BUNDLE");
char *endpoint;
char *env_ak = getenv("OSC_ACCESS_KEY");
char *env_sk = getenv("OSC_SECRET_KEY");
char user_agent[sizeof "osc-sdk-c/" + OSC_SDK_VERSON_L];
char *cert = getenv("OSC_X509_CLIENT_CERT");
char *sslkey = getenv("OSC_X509_CLIENT_KEY");
char *auth = getenv("OSC_AUTH_METHOD");

strcpy(stpcpy(user_agent, "osc-sdk-c/"), osc_sdk_version_str());
e->region = getenv("OSC_REGION");
e->flag = flag;
e->auth_method = flag & OSC_ENV_PASSWORD_AUTH ? OSC_PASSWORD_METHOD :
OSC_AKSK_METHOD;
endpoint = getenv("OSC_ENDPOINT_API");
osc_init_str(&e->endpoint);

if (auth && (!strcmp(auth, "password") || !strcmp(auth, "basic")))
e->auth_method = OSC_PASSWORD_METHOD;
else if (auth && strcmp(auth, "accesskey")) {
fprintf(stderr, "'%s' invalid authentication method\n", auth);
return -1;
}

if (!profile) {
profile = getenv("OSC_PROFILE");
e->ak = env_ak;
e->sk = env_sk;
if (e->auth_method == OSC_PASSWORD_METHOD) {
e->ak = getenv("OSC_LOGIN");
e->sk = getenv("OSC_PASSWORD");
} else {
e->ak = getenv("OSC_ACCESS_KEY");
e->sk = getenv("OSC_SECRET_KEY");
}
if (!profile && (!e->ak || !e->sk))
profile = "default";

}

if (profile) {
int f;

if (osc_load_ak_sk_from_conf(profile, &e->ak, &e->sk))
return -1;
e->flag |= OSC_ENV_FREE_AK_SK;
if (e->auth_method == OSC_PASSWORD_METHOD) {
if (osc_load_loging_password_from_conf(profile, &e->ak,
&e->sk) < 0)
return -1;
e->flag |= OSC_ENV_FREE_AK;
if (!e->sk)
e->sk = getenv("OSC_PASSWORD");
else
e->flag |= OSC_ENV_FREE_SK;
} else {
if (osc_load_ak_sk_from_conf(profile, &e->ak,
&e->sk) < 0)
return -1;
e->flag |= OSC_ENV_FREE_AK_SK;
}
if (!osc_load_region_from_conf(profile, &e->region))
e->flag |= OSC_ENV_FREE_REGION;
f = osc_load_cert_from_conf(profile, &e->cert, &e->sslkey);
Expand All @@ -337,20 +395,25 @@ int osc_init_sdk(struct osc_env *e, const char *profile, unsigned int flag)
osc_str_append_string(&e->endpoint, endpoint);
}

if (!e->ak || !e->sk) {
fprintf(stderr, "access key and secret key needed\n");
return -1;
}
if (e->auth_method == OSC_AKSK_METHOD) {
if (!e->ak || !e->sk) {
fprintf(stderr, "access key and secret key needed\n");
return -1;
}

if (strlen(e->ak) != AK_SIZE || strlen(e->sk) != SK_SIZE) {
fprintf(stderr, "Wrong access key or secret key size\n");
return -1;
if (strlen(e->ak) != AK_SIZE || strlen(e->sk) != SK_SIZE) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strnlen_s ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what it would check, I already check that ak/sk are not null.
if the string lack a 0 termination, then there is already nothing I can do.

So adding those check would only be useful, if there is an non-null-terminated string that go beyond strsz.
but I'm not sure the positive of this would overcome the negative of adding a C11 functions that might be less supported than strlen. (C11 glibc support is not the same as C11 compiler support)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made this issue outscale/osc-sdk-c#23

fprintf(stderr, "Wrong access key or secret key size\n");
return -1;
}
} else if (e->auth_method == OSC_PASSWORD_METHOD) {
if (!e->ak || !e->sk) {
fprintf(stderr, "login and password needed\n");
return -1;
}
}

e->headers = NULL;
stpcpy(stpcpy(stpcpy(ak_sk, e->ak), ":"), e->sk);
e->c = curl_easy_init();
e->headers = curl_slist_append(e->headers, "Content-Type: application/json");

/* Setting HEADERS */
if (flag & OSC_VERBOSE_MODE)
Expand All @@ -361,17 +424,35 @@ int osc_init_sdk(struct osc_env *e, const char *profile, unsigned int flag)
curl_easy_setopt(e->c, CURLOPT_SSLCERT, cert);
if (sslkey)
curl_easy_setopt(e->c, CURLOPT_SSLKEY, sslkey);
curl_easy_setopt(e->c, CURLOPT_HTTPHEADER, e->headers);
curl_easy_setopt(e->c, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(e->c, CURLOPT_USERAGENT, user_agent);

/* setting CA is CURL_CA_BUNDLE is set */
if (ca)
curl_easy_setopt(e->c, CURLOPT_CAINFO, ca);

e->headers = curl_slist_append(e->headers, "Content-Type: application/json");

/* For authentification we specify the method and our acces key / secret key */
curl_easy_setopt(e->c, CURLOPT_AWS_SIGV4, "osc");
curl_easy_setopt(e->c, CURLOPT_USERPWD, ak_sk);
if (e->auth_method == OSC_AKSK_METHOD) {
curl_easy_setopt(e->c, CURLOPT_AWS_SIGV4, "osc");
} else if (e->auth_method == OSC_PASSWORD_METHOD) {
time_t clock;
struct tm tm;
char time_hdr[TIME_HDR_KEY_L + TIMESTAMP_SIZE] = TIME_HDR_KEY;

time(&clock);
if (!gmtime_r(&clock, &tm)) {
fprintf(stderr, "gmtime_r fail\n");
return -1;
}
strftime(time_hdr + TIME_HDR_KEY_L - 1,
TIMESTAMP_SIZE, "%Y%m%dT%H%M%SZ", &tm);
e->headers = curl_slist_append(e->headers, time_hdr);
}
curl_easy_setopt(e->c, CURLOPT_HTTPHEADER, e->headers);
curl_easy_setopt(e->c, CURLOPT_USERNAME, e->ak);
curl_easy_setopt(e->c, CURLOPT_PASSWORD, e->sk);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if e->auth_method == OSC_AKSK_METHOD ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, ak/sk use CURLOPT_USERNAME/CURLOPT_PASSWORD for the ak and sk.
the curl_easy_setopt(e->c, CURLOPT_AWS_SIGV4, "osc"); is for v4 specific arguments.


return 0;
}
Expand All @@ -381,9 +462,11 @@ void osc_deinit_sdk(struct osc_env *e)
curl_slist_free_all(e->headers);
curl_easy_cleanup(e->c);
osc_deinit_str(&e->endpoint);
if (e->flag & OSC_ENV_FREE_AK_SK) {
if (e->flag & OSC_ENV_FREE_AK) {
free(e->ak);
e->ak = NULL;
}
if (e->flag & OSC_ENV_FREE_SK) {
free(e->sk);
e->sk = NULL;
}
Expand Down
24 changes: 18 additions & 6 deletions lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,33 @@ struct osc_str {
char *buf;
};

#define OSC_ENV_FREE_AK_SK 1
#define OSC_ENV_FREE_REGION 2
#define OSC_VERBOSE_MODE 4
#define OSC_INSECURE_MODE 8
#define OSC_ENV_FREE_CERT 16
#define OSC_ENV_FREE_SSLKEY 32
#define OSC_ENV_FREE_AK 1 << 0
#define OSC_ENV_FREE_REGION 1 << 1
#define OSC_VERBOSE_MODE 1 << 2 /* curl verbose mode + print request content */
#define OSC_INSECURE_MODE 1 << 3 /* see --insecure option of curl */
#define OSC_ENV_FREE_CERT 1 << 4
#define OSC_ENV_FREE_SSLKEY 1 << 5
#define OSC_ENV_FREE_SK 1 << 6
#define OSC_ENV_PASSWORD_AUTH 1 << 7 /* force password/login usage */

#define OSC_ENV_FREE_AK_SK (OSC_ENV_FREE_AK | OSC_ENV_FREE_SK)

#define OSC_API_VERSION "____api_version____"
#define OSC_SDK_VERSION ____sdk_version____

enum osc_auth_method {
OSC_AKSK_METHOD,
OSC_PASSWORD_METHOD
};

struct osc_env {
char *ak;
char *sk;
char *region;
char *cert;
char *sslkey;
int flag;
enum osc_auth_method auth_method;
struct curl_slist *headers;
struct osc_str endpoint;
CURL *c;
Expand Down Expand Up @@ -104,6 +114,8 @@ ____args____

int osc_load_ak_sk_from_conf(const char *profile, char **ak, char **sk);
int osc_load_region_from_conf(const char *profile, char **region);
int osc_load_loging_password_from_conf(const char *profile,
char **email, char **password);

/**
* @brief parse osc config file, and store cred_path/key_path. key is optional.
Expand Down
20 changes: 13 additions & 7 deletions main_tpl.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ ____complex_struct_func_parser____

int main(int ac, char **av)
{
auto_osc_env struct osc_env e;
auto_osc_str struct osc_str r;
auto_osc_env struct osc_env e = {0};
auto_osc_str struct osc_str r = {0};
int color_flag = 0;
int i;
char *help_appent = getenv("COGNAC_HELP_APPEND");
Expand All @@ -91,13 +91,17 @@ int main(int ac, char **av)
program_name = av[0];
else
++program_name;


for (i = 1; i < ac; ++i) {
if (!strcmp("--verbose", av[i])) {
flag |= OSC_VERBOSE_MODE;
} else if (!strcmp("--insecure", av[i])) {
flag |= OSC_INSECURE_MODE;
} else if (!strcmp("--insecure", av[i])) {
flag |= OSC_INSECURE_MODE;
} else if (!strcmp("--raw-print", av[i])) {
flag |= OAPI_RAW_OUTPUT;
} else if (!strcmp("--auth-password", av[i])) {
flag |= OSC_ENV_PASSWORD_AUTH;
} else if (!argcmp2("--profile", av[i], '=')) {
if (av[i][sizeof("--profile") - 1] == '=') {
profile = &av[i][sizeof("--profile")];
Expand All @@ -123,6 +127,7 @@ int main(int ac, char **av)
"options:\n"
"\t--insecure \tdoesn't verify SSL certificats\n"
"\t--raw-print \tdoesn't format the output\n"
"\t--auth-password \tuse password/login instead of AK/SK\n"
"\t--verbose \tcurl backend is now verbose\n"
"\t--profile=PROFILE \tselect profile"
"\t--help [CallName]\tthis, can be used with call name, example:\n\t\t\t\t%s --help ReadVms\n"
Expand All @@ -133,7 +138,10 @@ int main(int ac, char **av)
}

for (i = 1; i < ac; ++i) {
if (!strcmp("--verbose", av[i]) || !strcmp("--insecure", av[i])) {
if (!strcmp("--verbose", av[i]) || \
!strcmp("--insecure", av[i]) || \
!strcmp("--auth-password", av[i]) || \
!strcmp("--raw-print", av[i])) {
/* Avoid Unknow Calls */
} else if (!argcmp2("--profile", av[i], '=')) {
if (!av[i][sizeof("--profile") - 1]) {
Expand All @@ -154,8 +162,6 @@ int main(int ac, char **av)
}
}
goto show_help;
} else if (!strcmp("--raw-print", av[i])) {
program_flag |= OAPI_RAW_OUTPUT;
} else if (!strcmp("--color", av[i])) {
color_flag |= JSON_C_TO_STRING_COLOR;
} else
Expand Down