Blame SOURCES/0000-hashed-license.patch

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