1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
|
/* This program creates the token and certificate files for Amazon Devpay's
* Simple Token Service (STS). Right now you can then use those files with
* the S3 device. */
#include "amanda.h"
#include <curl/curl.h>
#include <glib.h>
#include <errno.h>
#include <getopt.h>
#include "base64.h"
#include "s3.h"
#define MAX_RESPONSE_SIZE (1024*1024)
typedef struct {
GString * user_token;
GString * access_key;
GString * secret_key;
} Credentials;
static void usage(void) {
g_fprintf(stderr,
"USAGE: activate-devpay KEY [ >> amanda.conf ]\n"
" This tool uses an Amazon Devpay activation key to retrieve an\n"
" user token, access key, and secret key for use with Amazon S3. Output\n"
" is in a form suitable for placement in an Amanda configuration file\n");
exit(EXIT_FAILURE);
}
/* This function is **not** thread-safe. Sorry. */
static const char * parse_commandline(int argc, char ** argv) {
if (argc != 2) {
usage();
return NULL;
} else {
return argv[1];
}
}
static char * activation_url(const char *key) {
char * url;
char * encoded_key;
encoded_key = curl_escape(key, 0);
url = g_strdup_printf(STS_BASE_URL "?Action=ActivateDesktopProduct&ActivationKey=%s&ProductToken=" STS_PRODUCT_TOKEN "&Version=2007-06-05", encoded_key);
curl_free(encoded_key);
return url;
}
/* This function is a CURLOPT_WRITEFUNCTION and a wrapper for
g_markup_parse_context_parse(). It's not very smart about errors. */
static size_t libcurl_gmarkup_glue(void *ptr, size_t size1, size_t size2,
void *stream) {
GMarkupParseContext *context = stream;
/* If this overflows, we have real problems, because we are expected to
* return the result of this multiplication in a size_t. */
size_t read_size = size1 * size2;
GError * error = NULL;
read_size = size1 * size2;
if (g_markup_parse_context_parse(context, ptr, read_size, &error)) {
return read_size;
} else {
if (error == NULL) {
g_fprintf(stderr, "Internal error parsing XML.\n");
} else {
g_fprintf(stderr, "Error parsing XML: %s\n",
error->message);
g_error_free(error);
}
exit(EXIT_FAILURE);
}
}
static void do_server_stuff(const char * key, GMarkupParseContext * parser) {
char * url;
CURL* handle;
char curl_error_buffer[CURL_ERROR_SIZE];
handle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_NOPROGRESS, TRUE);
curl_easy_setopt(handle, CURLOPT_NOSIGNAL, TRUE);
curl_easy_setopt(handle, CURLOPT_AUTOREFERER, TRUE);
curl_easy_setopt(handle, CURLOPT_ENCODING, ""); /* Support everything. */
#ifdef CURLOPT_MAXFILESIZE
curl_easy_setopt(handle, CURLOPT_MAXFILESIZE, MAX_RESPONSE_SIZE);
#endif
curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, curl_error_buffer);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, libcurl_gmarkup_glue);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, parser);
url = activation_url(key);
curl_easy_setopt(handle, CURLOPT_URL, url);
if (curl_easy_perform(handle) != 0) {
g_error("Problem fetching data from server:\n%s",
curl_error_buffer);
exit(EXIT_FAILURE);
}
g_free(url);
}
static void parser_got_text(GMarkupParseContext * context,
const char * text,
size_t text_len,
gpointer user_data,
GError ** error) {
Credentials * rval = user_data;
const char * current_tag = g_markup_parse_context_get_element(context);
g_assert(rval != NULL);
g_assert(*error == NULL);
/* We use strrstr instead of strcmp because Amazon uses namespaces
* that I don't want to deal with. */
if (g_strrstr(current_tag, "UserToken")) {
g_string_append_len(rval->user_token, text, text_len);
return;
} else if (g_strrstr(current_tag, "AWSAccessKeyId")) {
g_string_append_len(rval->access_key, text, text_len);
return;
} else if (g_strrstr(current_tag, "SecretAccessKey")) {
g_string_append_len(rval->secret_key, text, text_len);
return;
} else if (g_strrstr(current_tag, "Code")) {
/* Is it a code we know? */
if (g_str_has_prefix(text, "ExpiredActivationKey")) {
g_set_error(error, G_MARKUP_ERROR, -1,
"Activation key has expired; get a new one.");
} else if (g_str_has_prefix(text, "InvalidActivationKey")) {
g_set_error(error, G_MARKUP_ERROR, -1,
"Activation key is not valid; double-check.");
} else {
/* Do nothing; wait for the message. */
}
} else if (g_strrstr(current_tag, "Message")) {
g_set_error(error, G_MARKUP_ERROR, -1, "%.*s", (int)text_len, text);
}
}
static void parser_got_error(GMarkupParseContext * context G_GNUC_UNUSED,
GError * error,
gpointer user_data G_GNUC_UNUSED) {
g_fprintf (stderr, "Problem with Amazon response: %s\n", error->message);
exit(EXIT_FAILURE);
}
static GMarkupParseContext * parser_init(Credentials * credentials) {
static const GMarkupParser parser_settings = {
NULL, /* start_element */
NULL, /* end_element */
parser_got_text, /* text */
NULL, /* passthrough */
parser_got_error /* error */
};
bzero(credentials, sizeof(*credentials));
credentials->user_token = g_string_new("");
credentials->access_key = g_string_new("");
credentials->secret_key = g_string_new("");
return g_markup_parse_context_new(&parser_settings, 0, credentials, NULL);
}
static void parser_cleanup(GMarkupParseContext * context) {
GError * error = NULL;
g_markup_parse_context_end_parse(context, &error);
if (error != NULL) {
g_fprintf (stderr, "Unexpected end of Amazon response: %s\n",
error->message);
exit(EXIT_FAILURE);
}
g_markup_parse_context_free(context);
}
/* This function is responsible for the whole output thing. */
static void do_output(Credentials * rare) {
if (rare == NULL ||
rare->user_token == NULL || !rare->user_token->len ||
rare->access_key == NULL || !rare->access_key->len ||
rare->secret_key == NULL || !rare->secret_key->len) {
g_fprintf(stderr, "Missing authentication data in response!\n");
exit(EXIT_FAILURE);
}
g_printf("device-property \"S3_USER_TOKEN\" \"%s\"\n"
"device-property \"S3_ACCESS_KEY\" \"%s\"\n"
"device-property \"S3_SECRET_KEY\" \"%s\"\n",
rare->user_token->str, rare->access_key->str,
rare->secret_key->str);
}
int main(int argc, char ** argv) {
const char * key;
GMarkupParseContext * parser;
Credentials credentials;
key = parse_commandline(argc, argv);
curl_global_init(CURL_GLOBAL_ALL);
parser = parser_init(&credentials);
do_server_stuff(key, parser);
curl_global_cleanup();
parser_cleanup(parser);
do_output(&credentials);
return 0;
}
|