diff options
-rw-r--r-- | NEWS | 16 | ||||
-rw-r--r-- | doc/eclat.1man | 39 | ||||
-rw-r--r-- | lib/.gitignore | 4 | ||||
-rw-r--r-- | lib/Makefile.am | 10 | ||||
-rw-r--r-- | lib/json.h | 66 | ||||
-rw-r--r-- | lib/jsongrm.y | 316 | ||||
-rw-r--r-- | lib/jsonlex.l | 183 | ||||
-rw-r--r-- | lib/libeclat.h | 3 | ||||
-rw-r--r-- | lib/qcreat.c | 4 | ||||
-rw-r--r-- | lib/reqsign.c | 2 | ||||
-rw-r--r-- | lib/yytrans | 4 | ||||
-rw-r--r-- | src/accfile.c | 2 | ||||
-rw-r--r-- | src/cmdline.opt | 4 | ||||
-rw-r--r-- | src/ec2map.c | 2 | ||||
-rw-r--r-- | src/eclat.c | 17 | ||||
-rw-r--r-- | src/eclat.h | 4 | ||||
-rw-r--r-- | src/util.c | 96 | ||||
-rw-r--r-- | tests/.gitignore | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 1 | ||||
-rw-r--r-- | tests/tjson.c | 116 |
20 files changed, 833 insertions, 57 deletions
@@ -1,5 +1,5 @@ -Eclat NEWS -- history of user-visible changes. 2014-07-09 -Copyright (C) 2012-2014 Sergey Poznyakoff +Eclat NEWS -- history of user-visible changes. 2015-01-19 +Copyright (C) 2012-2015 Sergey Poznyakoff See the end of file for copying conditions. Please send Eclat bug reports to <gray+eclat@gnu.org.ua> @@ -18,7 +18,15 @@ The signature version 4 signing process is enabled by adding the following statement in the eclat configuration file: signature-version 4; - + +* If availability region is not supplied, it is read from the instance store. + +* IAM support + +If access key is not found in the access file, it is assumed to be a +IAM role name. The authentication credentials are then taken from the +instance store. + Version 1.0, 2013-12-20 @@ -32,7 +40,7 @@ First alpha release. ========================================================================= Copyright information: -Copyright (C) 2012, 2013 Sergey Poznyakoff +Copyright (C) 2012-2015 Sergey Poznyakoff Permission is granted to anyone to make or distribute verbatim copies of this document as received, in any medium, provided that the diff --git a/doc/eclat.1man b/doc/eclat.1man index 0d39f21..9560e81 100644 --- a/doc/eclat.1man +++ b/doc/eclat.1man @@ -1,5 +1,5 @@ .\" This file is part of Eclat -*- nroff -*- -.\" Copyright (C) 2012-2014 Sergey Poznyakoff +.\" Copyright (C) 2012-2015 Sergey Poznyakoff .\" .\" Eclat is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by @@ -13,7 +13,7 @@ .\" .\" You should have received a copy of the GNU General Public License .\" along with Eclat. If not, see <http://www.gnu.org/licenses/>. -.TH ECLAT 1 "July 10, 2014" "ECLAT" "Eclat User Reference" +.TH ECLAT 1 "January 19, 2015" "ECLAT" "Eclat User Reference" .SH NAME eclat \- EC2 Command Line Administrator Tool .SH SYNOPSIS @@ -26,7 +26,7 @@ eclat \- EC2 Command Line Administrator Tool is a tool that makes it possible to manage Amazon EC2 services from the command line. In contrast to the tools provided by Amazon itself, .B Eclat -does not require tons of resource-consuming libraries (\fBJava +does not require tons of resource-consuming libraries (\fIJava madness\fR), is very fast and efficient. .PP All administrative tasks are invoked through the single binary, @@ -67,15 +67,23 @@ argument to identify the action to be performed. It then forms an Amazon request using the rest arguments supplied to the command, and sends it to the selected endpoint. .PP -An \fBendpoint\fR is a URI of the Amazon server which is supposed to -handle the request. It is selected according to availability region, -as set in the configuration file (the \fBdefault\-region\fR statement), -or in the command line (the \fB\-\-region\fR option). If neither of -these is set, the endpoint specified by the \fBdefault\-endpoint\fR -configuration statement is used. +.I Availability region +specifies the region in the AWS where the requested resource is +located. It can be set either in the configuration file (the +\fBdefault\-region\fR statement), or in the command line (the +\fB\-\-region\fR option). +.PP +If avaialbility region is not set, \fBeclat\fR attempts to get it +from the instance store. This attempt will succeed only if it is +run on a EC2 instance. +.PP +An \fIendpoint\fR is a URI of the Amazon server which is supposed to +handle the request. It is selected according to the availability +region. The default value is provided in the configuration file +(using the \fBdefault\-endpoint\fR statement). .PP Upon completion of the action, Amazon sends back a -.BR response : +.IR response : an XML document containing details about the result of the operation performed. This document is displayed using a special format, expressed in eclat formatting language (\fBforlan\fR for short). A set of @@ -93,7 +101,7 @@ the option argument as its name. User-defined formats are declared in the configuration file using the \fBdefine\-format\fR statement. .PP If none of these options is given, the request -.B action name +.I action name is used to look up the default format to use. A default format is defined in the configuration file using the \fBformat\fR statement. .PP @@ -105,7 +113,7 @@ If .B eclat fails to select appropriate format using this procedure, it dumps the contents of the response on the standard output using -.BR "path notation" , +.IR "path notation" , where each tag is identified by its name and the names of parent tags, separated by dots. .SH AUTHENTICATION @@ -138,6 +146,13 @@ If the \fB\-\-access\-key\fR option is used, its argument is the access key or tag to look for in the access file. Otherwise, .B eclat selects the first available key pair. +.PP +If the access key is not found in the access file, it is assumed to be +a name of the \IIAM\fR role. The program then tries to obtain +credentials from the instance store. This attempt will succeed only +if \fBeclat\fR is run on an EC2 instance which is assigned a role upon +its creation (see +.BR http://docs.aws.amazon.com/IAM/latest/UserGuide/roles-usingrole-ec2instance.html ) .SH MAPS Each amazon resource has a unique string associated with it, called its diff --git a/lib/.gitignore b/lib/.gitignore index e0d9b22..e96121b 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -2,3 +2,7 @@ forlangrm.c forlangrm.h forlangrm.output forlanlex.c +jsongrm.c +jsongrm.h +jsongrm.output +jsonlex.c diff --git a/lib/Makefile.am b/lib/Makefile.am index f3a9d42..428de49 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -42,6 +42,10 @@ libeclat_a_SOURCES=\ forlangrm.h\ forlangrm.y\ forlanlex.l\ + json.h\ + jsongrm.h\ + jsongrm.y\ + jsonlex.l\ getans.c\ getyn.c\ hmac_sha1.c\ @@ -68,8 +72,14 @@ AM_CPPFLAGS = -I$(top_srcdir)/grecs/src/ $(CURL_CFLAGS) forlanlex.c: forlangrm.h forlangrm.c forlangrm.h: forlangrm.y +jsonlex.c: jsongrm.h +jsongrm.c jsongrm.h: jsongrm.y + AM_YFLAGS=-tdv AM_LFLAGS=-dp +LEXCOMPILE = $(top_srcdir)/@GRECS_SUBDIR@/build-aux/yyrename '$(LEX) $(LFLAGS) $(AM_LFLAGS)' +YACCCOMPILE = $(top_srcdir)/@GRECS_SUBDIR@/build-aux/yyrename '$(YACC) $(YFLAGS) $(AM_YFLAGS)' + diff --git a/lib/json.h b/lib/json.h new file mode 100644 index 0000000..36ca243 --- /dev/null +++ b/lib/json.h @@ -0,0 +1,66 @@ +/* This file is part of Eclat. + Copyright (C) 2012-2015 Sergey Poznyakoff. + + Eclat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Eclat is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ + +#include <grecs.h> + +enum json_object_type +{ + json_null, + json_bool, + json_number, + json_string, + json_arr, + json_obj +}; + +struct json_object; +struct json_array { + size_t oc; + struct json_object **ov; +}; + +struct json_object { + enum json_object_type type; + union { + int b; /* json_bool */ + double n; /* json_number */ + char *s; /* json_string */ + struct json_array *a; /* json_arr */ + struct grecs_symtab *o; /* json_o */ + } v; +}; + +struct json_pair { + char *k; + struct json_object *v; +}; + +extern char const *json_err_diag; +extern char const *json_err_ptr; +extern struct json_object *json_return_obj; + +void jsonlex_setup(char const *s, size_t l); +void jsonlex_diag(const char *s, size_t off); +struct json_object *json_object_create(int type); +struct grecs_symtab *json_assoc_create(void); +void json_object_free(struct json_object *obj); + +struct json_object *json_parse_string(char const *input, size_t len); + +struct json_object *json_object_lookup(struct json_object *obj, + const char *ident); + + diff --git a/lib/jsongrm.y b/lib/jsongrm.y new file mode 100644 index 0000000..398d2e7 --- /dev/null +++ b/lib/jsongrm.y @@ -0,0 +1,316 @@ +%{ +/* This file is part of Eclat. + Copyright (C) 2012-2015 Sergey Poznyakoff. + + Eclat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Eclat is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ + +#include "libeclat.h" +#include <string.h> +#include "jsongrm.h" +#include "json.h" + +struct json_object *json_return_obj; + +static int yyerror(char const *s); + +static struct json_pair * +pairdup(struct json_pair *in) +{ + struct json_pair *out = grecs_malloc(sizeof(*out)); + out->k = in->k; + out->v = in->v; + return out; +} + +static void +pairfree(void *ptr) +{ + struct json_pair *p = ptr; + free(p->k); + json_object_free(p->v); + free(p); +} + +static void +objfree(void *ptr) +{ + struct json_object *o = ptr; + json_object_free(o); +} + +%} +%token <n> T_NUMBER +%token <s> T_STRING +%token <b> T_BOOL +%token T_NULL T_ERR + +%type <a> array +%type <list> objects objlist pairs pairlist +%type <p> pair +%type <obj> object +%type <o> assoc + +%union { + int b; + double n; + char *s; + struct json_array *a; + struct grecs_symtab *o; + struct json_object *obj; + struct grecs_list *list; + struct json_pair *p; +} +%% + +input : object + { + json_return_obj = $1; + } + ; + +object : T_NUMBER + { + $$ = json_object_create(json_number); + $$->v.n = $1; + } + | T_STRING + { + $$ = json_object_create(json_string); + $$->v.s = $1; + } + | T_BOOL + { + $$ = json_object_create(json_bool); + $$->v.b = $1; + } + | T_NULL + { + $$ = json_object_create(json_null); + } + | array + { + $$ = json_object_create(json_arr); + $$->v.a = $1; + } + | assoc + { + $$ = json_object_create(json_obj); + $$->v.o = $1; + } + ; + +array : '[' objects ']' + { + struct json_array *a = grecs_malloc(sizeof(*a)); + if (!$2) { + a->oc = 0; + a->ov = NULL; + } else { + size_t i; + struct grecs_list_entry *ep; + a->oc = $2->count; + a->ov = grecs_calloc(a->oc, sizeof(a->ov)); + for (i = 0, ep = $2->head; ep; i++, ep = ep->next) { + struct json_object *p = ep->data; + a->ov[i] = p; + } + } + $$ = a; + } + ; + +objects : /* empty */ + { + $$ = NULL; + } + | objlist + ; + +objlist : object + { + $$ = grecs_list_create(); + $$->free_entry = objfree; + grecs_list_append($$, $1); + } + | objlist ',' object + { + grecs_list_append($1, $3); + } + ; + +assoc : '{' pairs '}' + { + struct grecs_symtab *s; + + s = json_assoc_create(); + if ($2) { + struct grecs_list_entry *ep; + for (ep = $2->head; ep; ep = ep->next) { + struct json_pair *p = ep->data; + int install = 1; + grecs_symtab_lookup_or_install(s, p, &install); + if (install) { + p->k = NULL; + p->v = NULL; + } + } + grecs_list_free($2); + } + $$ = s; + } + ; + +pairs : /* empty */ + { + $$ = NULL; + } + | pairlist + ; + +pairlist: pair + { + $$ = grecs_list_create(); + $$->free_entry = pairfree; + grecs_list_append($$, $1); + } + | pairlist ',' pair + { + grecs_list_append($1, $3); + } + ; + +pair : T_STRING ':' object + { + struct json_pair *p = grecs_malloc(sizeof(*p)); + p->k = $1; + p->v = $3; + $$ = p; + } + ; +%% + +static int +yyerror(char const *s) +{ + jsonlex_diag(s, 0); + return 0; +} + +struct json_object * +json_object_create(int type) +{ + struct json_object *obj = grecs_zalloc(sizeof(*obj)); + obj->type = type; + return obj; +} + +void +json_object_free(struct json_object *obj) +{ + size_t i; + + if (!obj) + return; + + switch (obj->type) { + case json_bool: + case json_number: + break; + case json_string: + free(obj->v.s); + break; + case json_arr: + for (i = 0; i < obj->v.a->oc; i++) + json_object_free(obj->v.a->ov[i]); + free(obj->v.a); + break; + case json_obj: + grecs_symtab_free(obj->v.o); + } + free(obj); +} + +static unsigned +json_st_hash(void *data, unsigned long n_buckets) +{ + struct json_pair *p = data; + return grecs_hash_string(p->k, n_buckets); +} + +static int +json_st_cmp(const void *a, const void *b) +{ + struct json_pair const *pa = a; + struct json_pair const *pb = b; + return strcmp(pa->k, pb->k); +} + +static int +json_st_copy(void *a, void *b) +{ + struct json_pair *pa = a; + struct json_pair *pb = b; + memcpy(pa, pb, sizeof(*pa)); + return 0; +} + +static void +json_st_free(void *ptr) +{ + struct json_pair *p = ptr; +// printf("FREE %s\n", p->k); + free(p->k); + json_object_free(p->v); + free(p); +} + +struct grecs_symtab * +json_assoc_create() +{ + return grecs_symtab_create(sizeof(struct json_pair), + json_st_hash, + json_st_cmp, + json_st_copy, + NULL, + json_st_free); +} + +struct json_object * +json_parse_string(char const *input, size_t len) +{ + jsonlex_setup(input, len); + if (yyparse()) { + /* FIXME: error recovery */ + return NULL; + } + return json_return_obj; +} + +struct json_object * +json_object_lookup(struct json_object *obj, const char *ident) +{ + struct json_pair key, *p; + if (!obj || obj->type != json_obj) + return NULL; + key.k = ident; + p = grecs_symtab_lookup_or_install(obj->v.o, &key, NULL); + if (!p) + return NULL; + return p->v; +} + + + + + diff --git a/lib/jsonlex.l b/lib/jsonlex.l new file mode 100644 index 0000000..322b05d --- /dev/null +++ b/lib/jsonlex.l @@ -0,0 +1,183 @@ +%{ +/* This file is part of Eclat. + Copyright (C) 2012-2015 Sergey Poznyakoff. + + Eclat is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Eclat is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ + +#include "libeclat.h" +#include "jsongrm.h" +#include "json.h" + +static char const *input_ptr; +static size_t input_size; + +char const *json_err_diag; +char const *json_err_ptr; + +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + do { \ + size_t n = (max_size > input_size) ? input_size : max_size; \ + if (n) { \ + memcpy(buf, input_ptr, n); \ + input_ptr += n; \ + input_size -= n; \ + } \ + result = n; \ + } while(0) + +void +jsonlex_setup(char const *s, size_t l) +{ + input_ptr = s; + input_size = l; + json_err_diag = NULL; + json_err_ptr = NULL; + yy_flex_debug = 0; +} + +void +jsonlex_diag(const char *s, size_t off) +{ + if (!json_err_diag) { + json_err_diag = s; + json_err_ptr = input_ptr - off; + } +} + +static int +utf8_wctomb(char *u) +{ + unsigned int wc = strtoul(u, NULL, 16); + int count; + char r[6]; + + if (wc < 0x80) + count = 1; + else if (wc < 0x800) + count = 2; + else if (wc < 0x10000) + count = 3; + else if (wc < 0x200000) + count = 4; + else if (wc < 0x4000000) + count = 5; + else if (wc <= 0x7fffffff) + count = 6; + else + return -1; + + switch (count) { + /* Note: code falls through cases! */ + case 6: + r[5] = 0x80 | (wc & 0x3f); + wc = wc >> 6; + wc |= 0x4000000; + case 5: + r[4] = 0x80 | (wc & 0x3f); + wc = wc >> 6; + wc |= 0x200000; + case 4: + r[3] = 0x80 | (wc & 0x3f); + wc = wc >> 6; + wc |= 0x10000; + case 3: + r[2] = 0x80 | (wc & 0x3f); + wc = wc >> 6; + wc |= 0x800; + case 2: + r[1] = 0x80 | (wc & 0x3f); + wc = wc >> 6; + wc |= 0xc0; + case 1: + r[0] = wc; + } + grecs_line_acc_grow(r, count); + return count; +} + +static int +unescape(int c, int *o) +{ + static char transtab[] = "\\\\\"\"//b\bf\fn\nr\rt\t"; + char *p; + + for (p = transtab; *p; p += 2) { + if (*p == c) { + *o = p[1]; + return 0; + } + } + return -1; +} + +#define YY_SKIP_YYWRAP 1 +static int +yywrap() +{ + return 1; +} +%} +D [0-9] +X [0-9a-fA-F] +%x STR +%% +"-"?{D}{D}*(.{D}{D}*)?([eE][-+]?{D}{D}*)? { + yylval.n = strtod(yytext, NULL); + return T_NUMBER; + } +\"[^\\\"]*\" { grecs_line_begin(); + grecs_line_add(yytext + 1, yyleng - 2); + yylval.s = grecs_line_finish(); + return T_STRING; } +\"[^\\\"]*\\{X}{4} { BEGIN(STR); + grecs_line_begin(); + grecs_line_add(yytext + 1, yyleng - 5); + utf8_wctomb(yytext + yyleng - 4); + } +\"[^\\\"]*\\. { int c; + BEGIN(STR); + grecs_line_begin(); + grecs_line_acc_grow(yytext + 1, yyleng - 2); + if (unescape(yytext[yyleng - 1], &c)) { + jsonlex_diag("invalid UTF-8 codepoint", 5); + return T_ERR; + } + grecs_line_acc_grow_char(c); + } +<STR>[^\\\"]*\" { BEGIN(INITIAL); + if (yyleng > 1) + grecs_line_acc_grow(yytext, yyleng - 1); + yylval.s = grecs_line_finish(); + return T_STRING; } +<STR>[^\\\"]*\\{X}{4} { + grecs_line_add(yytext, yyleng - 5); + utf8_wctomb(yytext + yyleng - 4); +} +<STR>[^\\\"]*\\. { + int c; + grecs_line_acc_grow(yytext, yyleng - 2); + if (unescape(yytext[yyleng - 1], &c)) { + jsonlex_diag("invalid UTF-8 codepoint", 5); + return T_ERR; + } + grecs_line_acc_grow_char(c); } + +null { return T_NULL; } +true { yylval.b = 1; return T_BOOL; } +false { yylval.b = 0; return T_BOOL; } +"{"|"}"|"["|"]"|":"|"," return yytext[0]; +[ \t\n]* ; +. { jsonlex_diag("bogus character", 0); + return T_ERR; } diff --git a/lib/libeclat.h b/lib/libeclat.h index b7dc189..2265a7b 100644 --- a/lib/libeclat.h +++ b/lib/libeclat.h @@ -86,12 +86,13 @@ struct ec2_query { struct grecs_list *headers; /* Query headers */ char *region; char *access_key; + char *token; unsigned long ttl; /* Time-to-live in seconds */ }; struct ec2_query *eclat_query_create(int flags, const char *endpoint, const char *uri, char const *region, - char const *access_key); + char const *access_key, char const *token); void eclat_query_free(struct ec2_query *); void eclat_query_add_param(struct ec2_query *q, const char *name, const char *value); diff --git a/lib/qcreat.c b/lib/qcreat.c index 32471a0..c2898a5 100644 --- a/lib/qcreat.c +++ b/lib/qcreat.c @@ -29,7 +29,8 @@ ec2_param_free(void *ptr) struct ec2_query * eclat_query_create(int flags, const char *endpoint, const char *uri, - char const *region, char const *access_key) + char const *region, char const *access_key, + char const *token) { struct ec2_query *q = grecs_zalloc(sizeof(*q)); q->flags = flags; @@ -45,5 +46,6 @@ eclat_query_create(int flags, const char *endpoint, const char *uri, q->ttl = 5; q->region = grecs_strdup(region ? region : "us-east-1"); q->access_key = grecs_strdup(access_key); + q->token = token ? grecs_strdup(token) : NULL; return q; } diff --git a/lib/reqsign.c b/lib/reqsign.c index 397f6c8..4250907 100644 --- a/lib/reqsign.c +++ b/lib/reqsign.c @@ -64,6 +64,8 @@ querysign2(struct ec2_query *req, char *secret) eclat_query_add_param(req, "AWSAccessKeyId", req->access_key); eclat_query_add_param(req, "SignatureMethod", "HmacSHA256"); eclat_query_add_param(req, "SignatureVersion", "2"); + if (req->token) + eclat_query_add_param(req, "SecurityToken", req->token); time(&t); strftime(tsbuf, sizeof(tsbuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t)); diff --git a/lib/yytrans b/lib/yytrans new file mode 100644 index 0000000..9e5979c --- /dev/null +++ b/lib/yytrans @@ -0,0 +1,4 @@ +forlangrm forlan_yy_ +forlanlex forlan_yy_ +jsongrm json_yy_ +jsonlex json_yy_ diff --git a/src/accfile.c b/src/accfile.c index 3b06582..84a786b 100644 --- a/src/accfile.c +++ b/src/accfile.c @@ -144,7 +144,7 @@ get_access_creds(const char *id, char **access_key_ptr, char **secret_key_ptr) int i; glob_t g; int rc = 1; - + if (!access_file_name) { debug(ECLAT_DEBCAT_MAIN, 1, ("no access file given")); return 1; diff --git a/src/cmdline.opt b/src/cmdline.opt index 073b7b7..665ec0e 100644 --- a/src/cmdline.opt +++ b/src/cmdline.opt @@ -1,5 +1,5 @@ /* This file is part of Eclat. - Copyright (C) 2012-2014 Sergey Poznyakoff. + Copyright (C) 2012-2015 Sergey Poznyakoff. Eclat is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -58,7 +58,7 @@ OPTIONS_BEGIN("eclat", [<COMMAND [COMMAND-OPTIONS...]>], [<gnu>], [<nopermute>], - [<copyright_year=2012, 2013>], + [<copyright_year=2012-2015>], [<copyright_holder=Sergey Poznyakoff>],[<nopermute>]) GROUP(Selecting program mode) diff --git a/src/ec2map.c b/src/ec2map.c index eb1d37b..03c3016 100644 --- a/src/ec2map.c +++ b/src/ec2map.c @@ -147,7 +147,7 @@ ec2_map_get(int dbg, int dir, void *data, const char *key, char **return_value) int rc; q = eclat_query_create(use_ssl ? EC2_QF_HTTPS : 0, endpoint, "/", - region_name, access_key); + region_name, access_key, security_token); eclat_query_add_param(q, "Action", map->action); env[0] = "key"; diff --git a/src/eclat.c b/src/eclat.c index 88592d2..5bbe7c4 100644 --- a/src/eclat.c +++ b/src/eclat.c @@ -26,6 +26,7 @@ char *endpoint = "ec2.amazonaws.com"; char *signature_version = "2"; char *access_key; char *secret_key; +char *security_token; char *region_name; int use_ssl; int ssl_verify_peer = 1; @@ -645,7 +646,8 @@ eclat_do_command(eclat_command_env_t *env, struct eclat_command *command, if (!(command->flags & CMD_NOQRY)) { env->query = eclat_query_create(use_ssl ? EC2_QF_HTTPS : 0, endpoint, "/", - region_name, access_key); + region_name, access_key, + security_token); } if (command->tag) @@ -769,9 +771,16 @@ main(int argc, char **argv) "cannot find endpoint for region %s", region_name); if (!secret_key) { - if (get_access_creds(access_key, &access_key, &secret_key)) - die(EX_UNAVAILABLE, - "cannot find authentication credentials"); + if (get_access_creds(access_key, &access_key, &secret_key)) { + if (!access_key) + die(EX_UNAVAILABLE, + "cannot find authentication credentials"); + else + eclat_get_instance_creds(access_key, + &access_key, + &secret_key, + &security_token); + } } debug(ECLAT_DEBCAT_MAIN, 1, ("using access key %s", access_key)); diff --git a/src/eclat.h b/src/eclat.h index b9c90ba..d3c0e8d 100644 --- a/src/eclat.h +++ b/src/eclat.h @@ -50,6 +50,7 @@ extern char *region_name; extern char *access_file_name; extern char *access_key; extern char *secret_key; +extern char *security_token; extern char *format_file; extern int translation_enabled; extern char *custom_map; @@ -173,6 +174,9 @@ void describe_query_update(eclat_command_env_t *env, int argc, char **argv, int eclat_send_query(CURL *curl, struct ec2_query *q); char *eclat_get_instance_zone(void); +void eclat_get_instance_creds(char *id, + char **access_key_ptr, char **secret_key_ptr, + char **token_ptr); int eclat_actcmp(const char *a, const char *b); @@ -15,6 +15,7 @@ along with Eclat. If not, see <http://www.gnu.org/licenses/>. */ #include "eclat.h" +#include "json.h" #include <termios.h> #include <sys/ioctl.h> @@ -430,41 +431,74 @@ char * eclat_get_instance_zone() { char *doc; + struct json_object *obj, *p; char *retval = NULL; - struct wordsplit ws; - size_t i; - int lev; doc = eclat_get_instance_meta_data(URL_INST_DOCUMENT); - - if (wordsplit(doc, &ws, - WRDSF_NOVAR | WRDSF_NOCMD | - WRDSF_SQUEEZE_DELIMS | - WRDSF_DQUOTE)) { - die(EX_UNAVAILABLE, "wordsplit: %s", wordsplit_strerror(&ws)); - } + + obj = json_parse_string(doc, strlen(doc)); + if (!obj) + die(EX_DATAERR, + "%s: near %s", + json_err_diag, + json_err_ptr); free(doc); - lev = 0; - for (i = 0; i < ws.ws_wordc; i++) { - if (strcmp(ws.ws_wordv[i], "{") == 0) - ++lev; - else if (strcmp(ws.ws_wordv[i], "}") == 0) - --lev; - else if (lev == 1 && strcmp(ws.ws_wordv[i], "region") == 0) { - if (i + 2 < ws.ws_wordc && - strcmp(ws.ws_wordv[i+1], ":") == 0) { - size_t len; - - retval = grecs_strdup(ws.ws_wordv[i+2]); - len = strlen(retval); - if (retval[len-1] == ',') - retval[len-1] = 0; - break; - } - } - } - wordsplit_free(&ws); - + p = json_object_lookup(obj, "region"); + if (p && p->type == json_string) + retval = grecs_strdup(p->v.s); + json_object_free(obj); return retval; } + +#define URL_INST_CREDS\ + "http://169.254.169.254/latest/meta-data/iam/security-credentials/" + +void +eclat_get_instance_creds(char *id, char **access_key_ptr, char **secret_key_ptr, + char **token_ptr) +{ + char *url = NULL; + size_t s; + char *doc; + struct json_object *obj, *p; + int err = 0; + + s = sizeof(URL_INST_CREDS) + strlen(id); + url = grecs_malloc(s); + strcpy(url, URL_INST_CREDS); + strcat(url, id); + + doc = eclat_get_instance_meta_data(url); + + obj = json_parse_string(doc, strlen(doc)); + if (!obj) + die(EX_DATAERR, + "%s: near %s", + json_err_diag, + json_err_ptr); + free(doc); + + p = json_object_lookup(obj, "AccessKeyId"); + if (p && p->type == json_string) + *access_key_ptr = grecs_strdup(p->v.s); + else + err = 1; + + p = json_object_lookup(obj, "SecretAccessKey"); + if (p && p->type == json_string) + *secret_key_ptr = grecs_strdup(p->v.s); + else + err = 1; + + p = json_object_lookup(obj, "Token"); + if (p && p->type == json_string) + *token_ptr = grecs_strdup(p->v.s); + else + err = 1; + + json_object_free(obj); + + if (err) + die(EX_DATAERR, "security credentials missing"); +} diff --git a/tests/.gitignore b/tests/.gitignore index 0f9880b..08adbd4 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -6,6 +6,7 @@ testsuite testsuite.dir testsuite.log tforlan +tjson thmac trws turlenc diff --gi |