#include #include #include #include #include #include #include #include "utils.h" #define MAX_LINE 2048 int main(int argc, char** argv) { const char* secret; secret = getenv("TRACKING_SECRET"); if (!secret) { fprintf(stderr, "TRACKING_SECRET not set\n"); return 1; } char input_line[MAX_LINE]; unsigned int hash_length = SHA_DIGEST_LENGTH; unsigned char input_hash[hash_length]; unsigned char expected_hash[hash_length]; int secret_length = strlen(secret); while (fgets(input_line, MAX_LINE, stdin) != NULL) { /* get the fields */ char *ip, *path, *query, *response_code, *unique_id; split_fields( input_line, &ip, &path, &query, &response_code, &unique_id, NO_MORE_FIELDS ); /* in the query string, grab the fields we want to verify */ char *id = NULL; char *hash = NULL; char *url = NULL; char *key, *value; while (parse_query_param(&query, &key, &value) >= 0) { if (strcmp(key, "id") == 0) { id = value; } else if (strcmp(key, "hash") == 0) { hash = value; } else if (strcmp(key, "url") == 0) { url = value; } } if (id == NULL || hash == NULL) continue; /* decode the params */ int id_length = url_decode(id); if (id_length < 0) continue; if (url_decode(hash) != 40) continue; int url_length = 0; if (url != NULL) { url_length = url_decode(url); if (url_length < 0) continue; } /* validation: * for clicks just check the response code--validation was done in the click redirect app * for impression pixels check the hash */ if (strcmp("/click", path) == 0) { if (strcmp(response_code, "302") != 0) { continue; } } else { /* turn the expected hash into bytes */ bool bad_hash = false; for (int i = 0; i < hash_length; i++) { int count = sscanf(&hash[i*2], "%2hhx", &input_hash[i]); if (count != 1) { bad_hash = true; break; } } if (bad_hash) continue; /* generate the expected hash */ HMAC_CTX ctx; // NOTE: EMR has openssl <1.0, so these HMAC methods don't return // error codes -- see https://www.openssl.org/docs/crypto/hmac.html HMAC_Init(&ctx, secret, secret_length, EVP_sha1()); HMAC_Update(&ctx, id, id_length); HMAC_Final(&ctx, expected_hash, &hash_length); /* check that the hashes match */ if (memcmp(input_hash, expected_hash, SHA_DIGEST_LENGTH) != 0) continue; } /* split out the fullname and subreddit if necessary */ char *fullname = id; char *subreddit = NULL; for (char *c = id; *c != '\0'; c++) { if (*c == '-') { subreddit = c + 1; *c = '\0'; break; } } /* output stuff! */ fputs(unique_id, stdout); fputc('\t', stdout); fputs(path, stdout); fputc('\t', stdout); fputs(fullname, stdout); fputc('\t', stdout); if (subreddit != NULL) { fputs(subreddit, stdout); } fputc('\n', stdout); } }