374 lines
13 KiB
Diff
374 lines
13 KiB
Diff
|
Patch by Robert Scheck <robert@fedoraproject.org> for geoipupdate <= 2.5.0 which backports
|
||
|
the support for hashed license keys. It is based on the following upstream commits:
|
||
|
|
||
|
1. https://github.com/maxmind/geoipupdate/commit/b7862460b6769f5c40b72de835b868882c3f3883
|
||
|
2. https://github.com/maxmind/geoipupdate/commit/4b41ea887e836ddb1a36c0c3a071ac49e8d71a25
|
||
|
3. https://github.com/maxmind/geoipupdate/commit/01fcfc1170dcedc35c758e111b8905cf6513d8b9
|
||
|
4. https://github.com/maxmind/geoipupdate/commit/6794f2eed0063a50749bf400e135f4f47ca2f465
|
||
|
5. https://github.com/maxmind/geoipupdate/commit/0670ee3e1237a72bae785877b354dbfe135de6be
|
||
|
6. https://github.com/maxmind/geoipupdate/commit/cddabc6f645ea3abbb8117ef0d03069df560fc36
|
||
|
7. https://github.com/maxmind/geoipupdate/commit/35a640087a4eca4e37306b717fa8513e26487b47
|
||
|
8. https://github.com/maxmind/geoipupdate/commit/642978928019f87181dd7c108b0363841c0135a9
|
||
|
9. https://github.com/maxmind/geoipupdate/commit/824ef039bbc8a2ed5a90d1da5a51d4a1639fdb09
|
||
|
|
||
|
--- geoipupdate-2.5.0/bin/geoipupdate.c 2017-10-30 15:38:24.000000000 +0100
|
||
|
+++ geoipupdate-2.5.0/bin/geoipupdate.c.licensekey 2023-04-13 21:23:51.340750299 +0200
|
||
|
@@ -22,6 +22,7 @@
|
||
|
enum gu_status {
|
||
|
GU_OK = 0,
|
||
|
GU_ERROR = 1,
|
||
|
+ GU_NO_UPDATE = 2,
|
||
|
};
|
||
|
|
||
|
typedef struct {
|
||
|
@@ -41,17 +42,14 @@
|
||
|
static int md5hex(const char *, char *);
|
||
|
static void common_req(CURL *, geoipupdate_s *);
|
||
|
static size_t get_expected_file_md5(char *, size_t, size_t, void *);
|
||
|
-static void
|
||
|
+static int
|
||
|
download_to_file(geoipupdate_s *, const char *, const char *, char *);
|
||
|
static long get_server_time(geoipupdate_s *);
|
||
|
static size_t mem_cb(void *, size_t, size_t, void *);
|
||
|
static in_mem_s *in_mem_s_new(void);
|
||
|
static void in_mem_s_delete(in_mem_s *);
|
||
|
-static in_mem_s *get(geoipupdate_s *, const char *);
|
||
|
-static void md5hex_license_ipaddr(geoipupdate_s *, const char *, char *);
|
||
|
static int update_database_general_all(geoipupdate_s *);
|
||
|
static int update_database_general(geoipupdate_s *, const char *);
|
||
|
-static int update_country_database(geoipupdate_s *);
|
||
|
static int gunzip_and_replace(geoipupdate_s const *const,
|
||
|
char const *const,
|
||
|
char const *const,
|
||
|
@@ -196,14 +194,12 @@
|
||
|
return GU_ERROR;
|
||
|
}
|
||
|
|
||
|
- err = (gu->license.account_id == NO_ACCOUNT_ID)
|
||
|
- ? update_country_database(gu)
|
||
|
- : update_database_general_all(gu);
|
||
|
+ err = update_database_general_all(gu);
|
||
|
}
|
||
|
geoipupdate_s_delete(gu);
|
||
|
}
|
||
|
curl_global_cleanup();
|
||
|
- return err ? GU_ERROR : GU_OK;
|
||
|
+ return err & GU_ERROR ? GU_ERROR : GU_OK;
|
||
|
}
|
||
|
|
||
|
static ssize_t my_getline(char **linep, size_t *linecapp, FILE *stream) {
|
||
|
@@ -242,8 +238,9 @@
|
||
|
say_if(up->verbose, "AccountID %d\n", up->license.account_id);
|
||
|
continue;
|
||
|
}
|
||
|
- if (sscanf(strt, "LicenseKey %12s", &up->license.license_key[0]) == 1) {
|
||
|
- say_if(up->verbose, "LicenseKey %s\n", up->license.license_key);
|
||
|
+ if (sscanf(strt, "LicenseKey %99s", &up->license.license_key[0]) == 1) {
|
||
|
+ say_if(
|
||
|
+ up->verbose, "LicenseKey %.4s...\n", up->license.license_key);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
@@ -534,15 +531,11 @@
|
||
|
// Make an HTTP request and download the response body to a file.
|
||
|
//
|
||
|
// If the HTTP status is not 2xx, we have a error message in the body rather
|
||
|
-// than a file. Write it to stderr and exit.
|
||
|
-//
|
||
|
-// TODO(wstorey@maxmind.com): Return boolean/int whether we succeeded rather
|
||
|
-// than exiting. Beyond being cleaner and easier to test, it will allow us to
|
||
|
-// clean up after ourselves better.
|
||
|
-static void download_to_file(geoipupdate_s *gu,
|
||
|
- const char *url,
|
||
|
- const char *fname,
|
||
|
- char *expected_file_md5) {
|
||
|
+// than a file. Write it to stderr and return an error.
|
||
|
+static int download_to_file(geoipupdate_s *gu,
|
||
|
+ const char *url,
|
||
|
+ const char *fname,
|
||
|
+ char *expected_file_md5) {
|
||
|
FILE *f = fopen(fname, "wb");
|
||
|
if (f == NULL) {
|
||
|
fprintf(stderr, "Can't open %s: %s\n", fname, strerror(errno));
|
||
|
@@ -553,6 +546,20 @@
|
||
|
CURL *curl = gu->curl;
|
||
|
|
||
|
expected_file_md5[0] = '\0';
|
||
|
+
|
||
|
+ char account_id[10] = {0};
|
||
|
+ int n = snprintf(account_id, 10, "%d", gu->license.account_id);
|
||
|
+ exit_if(n < 0,
|
||
|
+ "Error creating account ID string for %d: %s\n",
|
||
|
+ gu->license.account_id,
|
||
|
+ strerror(errno));
|
||
|
+ exit_if(n < 0 || n >= 10,
|
||
|
+ "An unexpectedly large account ID was encountered: %d\n",
|
||
|
+ gu->license.account_id);
|
||
|
+
|
||
|
+ curl_easy_setopt(curl, CURLOPT_USERNAME, account_id);
|
||
|
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, gu->license.license_key);
|
||
|
+
|
||
|
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_expected_file_md5);
|
||
|
curl_easy_setopt(curl, CURLOPT_HEADERDATA, expected_file_md5);
|
||
|
|
||
|
@@ -577,7 +584,19 @@
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
- if (status < 200 || status >= 300) {
|
||
|
+ if (status == 304) {
|
||
|
+ say_if(gu->verbose, "No new updates available\n");
|
||
|
+ unlink(fname);
|
||
|
+ return GU_NO_UPDATE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (status == 401) {
|
||
|
+ fprintf(stderr, "Your account ID or license key is invalid\n");
|
||
|
+ unlink(fname);
|
||
|
+ return GU_ERROR;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (status != 200) {
|
||
|
fprintf(stderr,
|
||
|
"Received an unexpected HTTP status code of %ld from %s:\n",
|
||
|
status,
|
||
|
@@ -589,7 +608,7 @@
|
||
|
free(message);
|
||
|
}
|
||
|
unlink(fname);
|
||
|
- exit(1);
|
||
|
+ return GU_ERROR;
|
||
|
}
|
||
|
|
||
|
// We have HTTP 2xx.
|
||
|
@@ -600,8 +619,9 @@
|
||
|
fprintf(stderr,
|
||
|
"Did not receive a valid expected database MD5 from server\n");
|
||
|
unlink(fname);
|
||
|
- exit(1);
|
||
|
+ return GU_ERROR;
|
||
|
}
|
||
|
+ return GU_OK;
|
||
|
}
|
||
|
|
||
|
// Retrieve the server file time for the previous HTTP request.
|
||
|
@@ -645,7 +665,17 @@
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-static in_mem_s *get(geoipupdate_s *gu, const char *url) {
|
||
|
+static int update_database_general(geoipupdate_s *gu, const char *edition_id) {
|
||
|
+ char *url = NULL, *geoip_filename = NULL, *geoip_gz_filename = NULL;
|
||
|
+ char hex_digest[33] = {0};
|
||
|
+
|
||
|
+ // Get the filename.
|
||
|
+ xasprintf(&url,
|
||
|
+ "%s://%s/app/update_getfilename?product_id=%s",
|
||
|
+ gu->proto,
|
||
|
+ gu->host,
|
||
|
+ edition_id);
|
||
|
+
|
||
|
in_mem_s *mem = in_mem_s_new();
|
||
|
|
||
|
say_if(gu->verbose, "url: %s\n", url);
|
||
|
@@ -663,47 +693,16 @@
|
||
|
long status = 0;
|
||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
|
||
|
|
||
|
- exit_if(status < 200 || status >= 300,
|
||
|
- "Received an unexpected HTTP status code of %ld from %s",
|
||
|
- status,
|
||
|
- url);
|
||
|
-
|
||
|
- return mem;
|
||
|
-}
|
||
|
-
|
||
|
-// Generate an MD5 hash of the concatenation of license key with IP address.
|
||
|
-//
|
||
|
-// This hash is suitable for the challenge parameter for downloading from the
|
||
|
-// /update_secure endpoint.
|
||
|
-static void md5hex_license_ipaddr(geoipupdate_s *gu,
|
||
|
- const char *client_ipaddr,
|
||
|
- char *new_digest_str) {
|
||
|
- unsigned char digest[16];
|
||
|
- MD5_CONTEXT context;
|
||
|
- md5_init(&context);
|
||
|
- md5_write(&context,
|
||
|
- (unsigned char *)gu->license.license_key,
|
||
|
- strlen(gu->license.license_key));
|
||
|
- md5_write(&context, (unsigned char *)client_ipaddr, strlen(client_ipaddr));
|
||
|
- md5_final(&context);
|
||
|
- memcpy(digest, context.buf, 16);
|
||
|
- for (int i = 0; i < 16; i++) {
|
||
|
- snprintf(&new_digest_str[2 * i], 3, "%02x", digest[i]);
|
||
|
+ if (status != 200) {
|
||
|
+ fprintf(stderr,
|
||
|
+ "Received an unexpected HTTP status code of %ld from %s\n",
|
||
|
+ status,
|
||
|
+ url);
|
||
|
+ free(url);
|
||
|
+ in_mem_s_delete(mem);
|
||
|
+ return GU_ERROR;
|
||
|
}
|
||
|
-}
|
||
|
-
|
||
|
-static int update_database_general(geoipupdate_s *gu, const char *edition_id) {
|
||
|
- char *url = NULL, *geoip_filename = NULL, *geoip_gz_filename = NULL,
|
||
|
- *client_ipaddr = NULL;
|
||
|
- char hex_digest[33] = {0}, hex_digest2[33] = {0};
|
||
|
|
||
|
- // Get the filename.
|
||
|
- xasprintf(&url,
|
||
|
- "%s://%s/app/update_getfilename?product_id=%s",
|
||
|
- gu->proto,
|
||
|
- gu->host,
|
||
|
- edition_id);
|
||
|
- in_mem_s *mem = get(gu, url);
|
||
|
free(url);
|
||
|
if (mem->size == 0) {
|
||
|
fprintf(stderr, "edition_id %s not found\n", edition_id);
|
||
|
@@ -718,63 +717,27 @@
|
||
|
md5hex(geoip_filename, hex_digest);
|
||
|
say_if(gu->verbose, "md5hex_digest: %s\n", hex_digest);
|
||
|
|
||
|
- // Look up our IP.
|
||
|
- xasprintf(&url, "%s://%s/app/update_getipaddr", gu->proto, gu->host);
|
||
|
- mem = get(gu, url);
|
||
|
- free(url);
|
||
|
-
|
||
|
- client_ipaddr = strdup(mem->ptr);
|
||
|
- if (NULL == client_ipaddr) {
|
||
|
- fprintf(stderr, "Unable to allocate memory for client IP address.\n");
|
||
|
- free(geoip_filename);
|
||
|
- in_mem_s_delete(mem);
|
||
|
- return GU_ERROR;
|
||
|
- }
|
||
|
-
|
||
|
- in_mem_s_delete(mem);
|
||
|
-
|
||
|
- say_if(gu->verbose, "Client IP address: %s\n", client_ipaddr);
|
||
|
-
|
||
|
- // Make the challenge MD5 hash.
|
||
|
- md5hex_license_ipaddr(gu, client_ipaddr, hex_digest2);
|
||
|
-
|
||
|
- free(client_ipaddr);
|
||
|
- say_if(gu->verbose, "md5hex_digest2 (challenge): %s\n", hex_digest2);
|
||
|
-
|
||
|
// Download.
|
||
|
xasprintf(&url,
|
||
|
- "%s://%s/app/"
|
||
|
- "update_secure?db_md5=%s&challenge_md5=%s&user_id=%d&edition_id=%"
|
||
|
- "s",
|
||
|
+ "%s://%s/geoip/databases/%s/update?db_md5=%s",
|
||
|
gu->proto,
|
||
|
gu->host,
|
||
|
- hex_digest,
|
||
|
- hex_digest2,
|
||
|
- gu->license.account_id,
|
||
|
- edition_id);
|
||
|
+ edition_id,
|
||
|
+ hex_digest);
|
||
|
xasprintf(&geoip_gz_filename, "%s.gz", geoip_filename);
|
||
|
|
||
|
char expected_file_md5[33] = {0};
|
||
|
- download_to_file(gu, url, geoip_gz_filename, expected_file_md5);
|
||
|
+ int rc = download_to_file(gu, url, geoip_gz_filename, expected_file_md5);
|
||
|
free(url);
|
||
|
|
||
|
- // Was there actually an update? We can tell because if not we will have
|
||
|
- // the same MD5 reported back. Note in the past we would check the response
|
||
|
- // body which does still say whether we have an update.
|
||
|
- if (strcmp(hex_digest, expected_file_md5) == 0) {
|
||
|
- say_if(gu->verbose, "No new updates available\n");
|
||
|
- unlink(geoip_gz_filename);
|
||
|
- free(geoip_filename);
|
||
|
- free(geoip_gz_filename);
|
||
|
- return GU_OK;
|
||
|
- }
|
||
|
-
|
||
|
- long filetime = -1;
|
||
|
- if (gu->preserve_file_times) {
|
||
|
- filetime = get_server_time(gu);
|
||
|
+ if (rc == GU_OK) {
|
||
|
+ long filetime = -1;
|
||
|
+ if (gu->preserve_file_times) {
|
||
|
+ filetime = get_server_time(gu);
|
||
|
+ }
|
||
|
+ rc = gunzip_and_replace(
|
||
|
+ gu, geoip_gz_filename, geoip_filename, expected_file_md5, filetime);
|
||
|
}
|
||
|
- int rc = gunzip_and_replace(
|
||
|
- gu, geoip_gz_filename, geoip_filename, expected_file_md5, filetime);
|
||
|
|
||
|
free(geoip_gz_filename);
|
||
|
free(geoip_filename);
|
||
|
@@ -790,53 +753,6 @@
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
-static int update_country_database(geoipupdate_s *gu) {
|
||
|
- char *geoip_filename = NULL, *geoip_gz_filename = NULL, *url = NULL;
|
||
|
- char hex_digest[33] = {0};
|
||
|
-
|
||
|
- xasprintf(&geoip_filename, "%s/GeoIP.dat", gu->database_dir);
|
||
|
- xasprintf(&geoip_gz_filename, "%s/GeoIP.dat.gz", gu->database_dir);
|
||
|
-
|
||
|
- // Calculate the MD5 hash of the database we currently have, if any. We get
|
||
|
- // back a zero MD5 hash if we don't have it yet.
|
||
|
- md5hex(geoip_filename, hex_digest);
|
||
|
- say_if(gu->verbose, "md5hex_digest: %s\n", hex_digest);
|
||
|
-
|
||
|
- xasprintf(&url,
|
||
|
- "%s://%s/app/update?license_key=%s&md5=%s",
|
||
|
- gu->proto,
|
||
|
- gu->host,
|
||
|
- &gu->license.license_key[0],
|
||
|
- hex_digest);
|
||
|
-
|
||
|
- char expected_file_md5[33] = {0};
|
||
|
- download_to_file(gu, url, geoip_gz_filename, expected_file_md5);
|
||
|
- free(url);
|
||
|
-
|
||
|
- // Was there actually an update? We can tell because if not we will have
|
||
|
- // the same MD5 reported back. Note in the past we would check the response
|
||
|
- // body which does still say whether we have an update.
|
||
|
- if (strcmp(hex_digest, expected_file_md5) == 0) {
|
||
|
- say_if(gu->verbose, "No new updates available\n");
|
||
|
- unlink(geoip_gz_filename);
|
||
|
- free(geoip_filename);
|
||
|
- free(geoip_gz_filename);
|
||
|
- return GU_OK;
|
||
|
- }
|
||
|
-
|
||
|
- long filetime = -1;
|
||
|
- if (gu->preserve_file_times) {
|
||
|
- filetime = get_server_time(gu);
|
||
|
- }
|
||
|
- int rc = gunzip_and_replace(
|
||
|
- gu, geoip_gz_filename, geoip_filename, expected_file_md5, filetime);
|
||
|
-
|
||
|
- free(geoip_gz_filename);
|
||
|
- free(geoip_filename);
|
||
|
-
|
||
|
- return rc;
|
||
|
-}
|
||
|
-
|
||
|
// Decompress the compressed database and move it into place in the database
|
||
|
// directory.
|
||
|
//
|
||
|
--- geoipupdate-2.5.0/bin/geoipupdate.h 2017-10-30 15:38:24.000000000 +0100
|
||
|
+++ geoipupdate-2.5.0/bin/geoipupdate.h.licensekey 2023-04-13 20:59:03.483969697 +0200
|
||
|
@@ -12,7 +12,11 @@
|
||
|
|
||
|
typedef struct {
|
||
|
int account_id;
|
||
|
- char license_key[13];
|
||
|
+ // For a long time, license keys were restricted to 12 characters. However,
|
||
|
+ // we want to change this for newer license keys. The array size is
|
||
|
+ // arbitrarily 100 as that seems big enough to hold any future license
|
||
|
+ // key.
|
||
|
+ char license_key[100];
|
||
|
edition_s *first;
|
||
|
} license_s;
|
||
|
|