diff options
author | Daniel Baumann <daniel@debian.org> | 2024-11-21 17:49:05 +0100 |
---|---|---|
committer | Daniel Baumann <daniel@debian.org> | 2024-11-21 17:49:05 +0100 |
commit | 4a8e45026e0f5de7d9711cfe1ddd23518b1d17db (patch) | |
tree | 3f0f2bf5c60c901954649edcbf7611328575ff29 /src | |
parent | Initial commit. (diff) | |
download | cryptopant-4a8e45026e0f5de7d9711cfe1ddd23518b1d17db.tar.xz cryptopant-4a8e45026e0f5de7d9711cfe1ddd23518b1d17db.zip |
Adding upstream version 1.3.1.upstream/1.3.1upstream
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/config.h.in | 138 | ||||
-rw-r--r-- | src/cryptopANT.c | 847 | ||||
-rw-r--r-- | src/cryptopANT.h | 92 | ||||
-rw-r--r-- | src/scramble_ips.c | 487 |
4 files changed, 1564 insertions, 0 deletions
diff --git a/src/config.h.in b/src/config.h.in new file mode 100644 index 0000000..dfbc598 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,138 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +#undef HAVE_LIBCRYPTO + +/* Define to 1 if you have the <openssl/aes.h> header file. */ +#undef HAVE_OPENSSL_AES_H + +/* Define to 1 if you have the <openssl/blowfish.h> header file. */ +#undef HAVE_OPENSSL_BLOWFISH_H + +/* Define to 1 if you have the <openssl/core_names.h> header file. */ +#undef HAVE_OPENSSL_CORE_NAMES_H + +/* Define to 1 if you have the <openssl/err.h> header file. */ +#undef HAVE_OPENSSL_ERR_H + +/* Define to 1 if you have the <openssl/evp.h> header file. */ +#undef HAVE_OPENSSL_EVP_H + +/* Define to 1 if you have the <openssl/md5.h> header file. */ +#undef HAVE_OPENSSL_MD5_H + +/* Define to 1 if you have the <openssl/provider.h> header file. */ +#undef HAVE_OPENSSL_PROVIDER_H + +/* Define to 1 if you have the <openssl/sha.h> header file. */ +#undef HAVE_OPENSSL_SHA_H + +/* Define to 1 if you have the `regcomp' function. */ +#undef HAVE_REGCOMP + +/* Define to 1 if you have the `regexec' function. */ +#undef HAVE_REGEXEC + +/* Define to 1 if you have the <regex.h> header file. */ +#undef HAVE_REGEX_H + +/* S6_ADDR32 is present in in6_addr when defined */ +#undef HAVE_S6_ADDR32 + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdio.h> header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* U6_ADDR32 is present in in6_addr when defined */ +#undef HAVE__U6_ADDR32 + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>, + <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT32_T + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `unsigned int' if <sys/types.h> does not define. */ +#undef size_t + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef uint32_t diff --git a/src/cryptopANT.c b/src/cryptopANT.c new file mode 100644 index 0000000..e140f24 --- /dev/null +++ b/src/cryptopANT.c @@ -0,0 +1,847 @@ +/* -*- Mode:C; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ +/* + * Copyright (C) 2004-2024 by the University of Southern California + * $Id: cdc84b9fca5b7bc01d665de67bbe6358d0a8131f $ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <stdio.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <fcntl.h> +#include <stdint.h> +#include <ctype.h> +#include <stdlib.h> + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> + +#include <openssl/evp.h> +#include <openssl/md5.h> +#include <openssl/blowfish.h> +#include <openssl/sha.h> +#include <openssl/aes.h> +#include <openssl/err.h> +#include <openssl/core_names.h> +#include <openssl/provider.h> + +#include "cryptopANT.h" +#include "config.h" + +#define MAX_BLK_LENGTH 32 +#define CACHE_BITS 24 /* How many bits of IPv4 we cache, cannot be zero */ +#define BF_KEYLEN 16 /* bytes */ + +#define TEST_CACHE 0 + +#define RESET_ETHER_MCAST(p) (*(char*)(p) &= 0xfe) + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#if HAVE__U6_ADDR32 +#define s6_addr32 __u6_addr.__u6_addr32 +#endif + +//determined by autoconf +#ifdef WORDS_BIGENDIAN +//sigh, older version of the code was not byte-order safe; this is needed +//to ensure backward compatibility AND compatibility with BE-systems. +#include <byteswap.h> +#define cryptopant_swap32(x) bswap_32(x) +#else +#define cryptopant_swap32(x) (x) +#endif + +typedef struct ipv4_hash_blk_ { + uint32_t ip4; + uint8_t pad[MAX_BLK_LENGTH - sizeof(uint32_t)]; +} ipv4_hash_blk_t; + +typedef struct ipv6_hash_blk_ { + struct in6_addr ip6; + uint8_t pad[MAX_BLK_LENGTH - sizeof(struct in6_addr)]; +} ipv6_hash_blk_t; + + +uint8_t scramble_ether_addr[ETHER_ADDR_LEN]; +uint16_t scramble_ether_vlan; +int scramble_mac; + +static int readhexstring (FILE *, u_char *, int *); + +static uint32_t ip4cache[1<<CACHE_BITS]; +static uint32_t ip4pad; /* first 4 bytes of pad */ +static uint32_t ip6pad[4]; +static u_char scramble_mac_buf[MAX_BLK_LENGTH]; +static u_char scramble_key[MAX_BLK_LENGTH]; + +static uint8_t ivec[64]; + +/* statistics */ +static long ipv4_cache_hits = 0; +static long ipv4_anon_calls = 0; +static long ipv6_anon_calls = 0; + + +static ipv4_hash_blk_t b4_in, b4_out; +static ipv6_hash_blk_t b6_in, b6_out; + +static scramble_crypt_t scramble_crypto4 = SCRAMBLE_AES; +static scramble_crypt_t scramble_crypto6 = SCRAMBLE_AES; + +/* openssl EVP pointers */ +static EVP_CIPHER_CTX *ctx4, *ctx6; +static EVP_CIPHER *cipher4, *cipher6; +static EVP_MD_CTX *mdctx4, *mdctx6; +static EVP_MD *md4, *md6; + +static struct { + char *name; + scramble_crypt_t type; +} scramble_names[] = { + { "md5", SCRAMBLE_MD5 }, + { "blowfish", SCRAMBLE_BLOWFISH }, + { "aes", SCRAMBLE_AES }, + { "sha", SCRAMBLE_SHA1 }, +}; + +const char * +scramble_type2name(scramble_crypt_t t) +{ + int i; + for (i = 0; i < sizeof(scramble_names)/sizeof(scramble_names[0]); ++i) + if (scramble_names[i].type == t) + return scramble_names[i].name; + return NULL; +} + +scramble_crypt_t +scramble_name2type(const char *name) +{ + int i; + for (i = 0; i < sizeof(scramble_names)/sizeof(scramble_names[0]); ++i) + if (strcasecmp(name, scramble_names[i].name) == 0) + return scramble_names[i].type; + return SCRAMBLE_NONE; +} + +scramble_crypt_t +scramble_crypto_ip4(void) +{ + return scramble_crypto4; +} + +scramble_crypt_t +scramble_crypto_ip6(void) +{ + return scramble_crypto6; +} + +int +scramble_newkey(u_char *key, int klen) +{ + FILE *rnd = fopen(SCRAMBLE_RANDOM_DEV, "r"); + if (rnd == NULL) { + perror("scramble_newkey(): fopen"); + return -1; + } + if (fread(key, 1, klen, rnd) != klen) { + perror("scramble_newkey(): fread"); + fclose(rnd); + return -1; + } + fclose(rnd); + return 0; +} + +int +scramble_newpad(u_char *pad, int plen) +{ + FILE *rnd = fopen(SCRAMBLE_RANDOM_DEV, "r"); + if (rnd == NULL) { + perror("scramble_newpad(): fopen"); + return -1; + } + if (fread(pad, 1, plen, rnd) != plen) { + perror("scramble_newpad(): fread"); + fclose(rnd); + return -1; + } + fclose(rnd); + return 0; +} + +int +scramble_newmac(u_char *mac, int mlen) +{ + FILE *rnd = fopen(SCRAMBLE_RANDOM_DEV, "r"); + if (rnd == NULL) { + perror("scramble_newkey(): fopen"); + return -1; + } + if (fread(mac, 1, mlen, rnd) != mlen) { + perror("scramble_newkey(): fread"); + fclose(rnd); + return -1; + } + fclose(rnd); + return 0; +} + +int +scramble_newiv(u_char *iv, int ivlen) +{ + FILE *rnd = fopen(SCRAMBLE_RANDOM_DEV, "r"); + if (rnd == NULL) { + perror("scramble_newiv(): fopen"); + return -1; + } + if (fread(iv, 1, ivlen, rnd) != ivlen) { + perror("scramble_newiv(): fread"); + fclose(rnd); + return -1; + } + fclose(rnd); + return 0; +} + +/* read a hex string from fd at current position and store it in s */ +static int +readhexstring(FILE *f, u_char *s, int *len) +{ + char c = 0; + int i; + for (i = 0; i < *len + 1; ++i) { + switch (fread(&c, 1, 1, f)) { + case 0: + *len = i; + return 0; + case 1: + break; + default: + return -1; + } + if (!isxdigit(c)) { + *len = i; + return 0; + } + s[i] = ((isdigit(c)) ? c - '0' : tolower(c) - 'a' + 10) << 4; + if (fread(&c, 1, 1, f) != 1) { + *len = i; + return -1; /* error: a byte has 2 digits */ + } + if (!isxdigit(c)) { + *len = i; + return -1; + } + s[i] |= (isdigit(c)) ? c - '0' : tolower(c) - 'a' + 10; + } + if (i == *len + 1) + return -1; /* means buffer is too short */ + return 0; +} + +int +scramble_readstate(const char *fn, scramble_state_t *s) +{ + u_char c4, c6; + int l4 = 1, l6 = 1; + FILE *f = fopen(fn, "r"); + if (f == NULL) { + perror("scramble_readstate(): fopen"); + return -1; + } + if (readhexstring(f, (u_char*)&c4, &l4) != 0) { + fprintf(stderr, "scramble_readstate(): error reading c4"); + fclose(f); + return -1; + } + assert(l4 == 1); + s->c4 = (scramble_crypt_t)c4; + if (readhexstring(f, (u_char*)&c6, &l6) != 0) { + fprintf(stderr, "scramble_readstate(): error reading c6"); + fclose(f); + return -1; + } + assert(l6 == 1); + s->c6 = (scramble_crypt_t)c6; + if (readhexstring(f, s->key, &s->klen) != 0) { + fprintf(stderr, "scramble_readstate(): error reading key"); + fclose(f); + return -1; + } + if (readhexstring(f, s->pad, &s->plen) != 0) { + fprintf(stderr, "scramble_readstate(): error reading pad"); + fclose(f); + return -1; + } + if (readhexstring(f, s->mac, &s->mlen) != 0) { + fprintf(stderr, "scramble_readstate(): error reading mac"); + fclose(f); + return -1; + } + if (readhexstring(f, s->iv, &s->ivlen) != 0) { + fprintf(stderr, "scramble_readstate(): error reading iv"); + fclose(f); + return -1; + } + fclose(f); + return 0; +} + +int +scramble_savestate(const char *fn, const scramble_state_t *s) +{ + int i; + /* set restrictive mode */ + int fd = creat(fn, S_IRUSR|S_IWUSR); + if (fd < 0) { + perror("scramble_savestate(): open"); + return -1; + } + FILE *f = fdopen(fd, "w"); + if (f == NULL) { + perror("scramble_savestate(): fopen"); + return -1; + } + if (fprintf(f, "%02x:%02x:", (unsigned)s->c4, (unsigned)s->c6) < 0) { + perror("scramble_savestate(): error saving cryptos"); + fclose(f); + return -1; + } + for (i = 0; i < s->klen; ++i) { + if (fprintf(f, "%02x", s->key[i]) < 0) { + perror("scramble_savestate(): error saving key"); + fclose(f); + return -1; + } + } + fprintf(f, ":"); + for (i = 0; i < s->plen; ++i) { + if (fprintf(f, "%02x", s->pad[i]) < 0) { + perror("scramble_savestate(): error saving pad"); + fclose(f); + return -1; + } + } + fprintf(f, ":"); + for (i = 0; i < s->mlen; ++i) { + if (fprintf(f, "%02x", s->mac[i]) < 0) { + perror("scramble_savestate(): error saving mac"); + fclose(f); + return -1; + } + } + + fprintf(f, ":"); + for (i = 0; i < s->ivlen; ++i) { + if (fprintf(f, "%02x", s->iv[i]) < 0) { + perror("scramble_savestate(): error saving lv"); + fclose(f); + return -1; + } + } + fprintf(f, "\n"); + fclose(f); + return 0; +} + +void +scramble_cleanup() +{ + // openssl evp cleanup + if (ctx4 != NULL) { + EVP_CIPHER_CTX_free(ctx4); + ctx4 = NULL; + } + if (ctx6 != NULL) { + EVP_CIPHER_CTX_free(ctx6); + ctx6 = NULL; + } + if (mdctx4 != NULL) { + EVP_MD_CTX_free(mdctx4); + mdctx4 = NULL; + } + if (mdctx6 != NULL) { + EVP_MD_CTX_free(mdctx6); + mdctx6 = NULL; + } + if (cipher4 != NULL) { + EVP_CIPHER_free(cipher4); + cipher4 = NULL; + } + if (cipher6 != NULL) { + EVP_CIPHER_free(cipher6); + cipher6 = NULL; + } + if (md4 != NULL) { + EVP_MD_free(md4); + md4 = NULL; + } + if (md6 != NULL) { + EVP_MD_free(md6); + md6 = NULL; + } +} + + +int +scramble_init(const scramble_state_t *s) +{ + int plen; + if (s->plen > MAX_BLK_LENGTH) + plen = MAX_BLK_LENGTH; + else + plen = s->plen; + + scramble_crypto4 = s->c4; + scramble_crypto6 = s->c6; + + memcpy(&b4_in, s->pad, plen); + ip4pad = cryptopant_swap32(b4_in.ip4); + + memcpy(&b6_in, s->pad, s->plen); + ip6pad[0] = b6_in.ip6.s6_addr32[0]; + ip6pad[1] = b6_in.ip6.s6_addr32[1]; + ip6pad[2] = b6_in.ip6.s6_addr32[2]; + ip6pad[3] = b6_in.ip6.s6_addr32[3]; + + memcpy(scramble_key, s->key, s->klen); + + // create contexts (will not need them all) + ctx4 = EVP_CIPHER_CTX_new(); + ctx6 = EVP_CIPHER_CTX_new(); + mdctx4 = EVP_MD_CTX_new(); + mdctx6 = EVP_MD_CTX_new(); + + // fetch ciphers and digests + void *res_ctx = NULL, *res_crypt = NULL; + switch(s->c4) { + case SCRAMBLE_AES: + OSSL_PROVIDER_load(NULL, "default"); + cipher4 = EVP_CIPHER_fetch(NULL, "AES-128-ECB", "provider=default"); + res_ctx = ctx4; + res_crypt = cipher4; + break; + case SCRAMBLE_BLOWFISH: + OSSL_PROVIDER_load(NULL, "legacy"); + cipher4 = EVP_CIPHER_fetch(NULL, "BF-ECB", "provider=legacy"); + if (cipher4 == NULL) { + cipher4 = EVP_CIPHER_fetch(NULL, "BF-ECB", NULL); + } + res_ctx = ctx4; + res_crypt = cipher4; + break; + case SCRAMBLE_SHA1: + md4 = EVP_MD_fetch(NULL, "SHA1", NULL); + res_ctx = mdctx4; + res_crypt = md4; + break; + case SCRAMBLE_MD5: + md4 = EVP_MD_fetch(NULL, "MD5", NULL); + res_ctx = mdctx4; + res_crypt = md4; + break; + case SCRAMBLE_NONE: + break; + default: + fprintf(stderr, + "scramble_init(): unsupported ipv4 scrambling crypto: %d\n", s->c4); + return -1; + } + if (s->c4 != SCRAMBLE_NONE && (res_ctx == NULL || res_crypt == NULL)) { + fprintf(stderr, + "scramble_init(): EVP ip4 init failures %p %p\n", res_ctx, res_crypt); + return -1; + } + + res_ctx = res_crypt = NULL; + + switch(s->c6) { + case SCRAMBLE_AES: + OSSL_PROVIDER_load(NULL, "default"); + cipher6 = EVP_CIPHER_fetch(NULL, "AES-128-ECB", "provider=default"); + res_ctx = ctx6; + res_crypt = cipher6; + break; + case SCRAMBLE_BLOWFISH: + OSSL_PROVIDER_load(NULL, "legacy"); + cipher6 = EVP_CIPHER_fetch(NULL, "BF-CBC", "provider=legacy"); + if (cipher6 == NULL) { + cipher6 = EVP_CIPHER_fetch(NULL, "BF-ECB", NULL); + } + res_ctx = ctx6; + res_crypt = cipher6; + break; + case SCRAMBLE_SHA1: + md6 = EVP_MD_fetch(NULL, "SHA1", NULL); + res_ctx = mdctx6; + res_crypt = md6; + break; + case SCRAMBLE_MD5: + md6 = EVP_MD_fetch(NULL, "MD5", NULL); + res_ctx = mdctx6; + res_crypt = md6; + break; + case SCRAMBLE_NONE: + break; + default: + fprintf(stderr, + "scramble_init(): unsupported ipv6 scrambling crypto: %d\n", s->c6); + return -1; + + } + if (s->c6 != SCRAMBLE_NONE && (res_ctx == NULL || res_crypt == NULL)) { + fprintf(stderr, + "scramble_init(): EVP ip6 init failures %p %p\n", res_ctx, res_crypt); + return -1; + } + if (cipher4 != NULL) { + if (!EVP_EncryptInit_ex2(ctx4, cipher4, scramble_key, ivec, NULL)) { + fprintf(stderr, + "scramble_init(): EVP_EncryptInit_ex2 failed:"); + ERR_print_errors_fp(stderr); + return -1; + } + } + if (cipher6 != NULL) { + if (!EVP_EncryptInit_ex2(ctx6, cipher6, scramble_key, ivec, NULL)) { + fprintf(stderr, + "scramble_init(): EVP_EncryptInit_ex2 failed:"); + ERR_print_errors_fp(stderr); + return -1; + } + } + // don't need to init anything for digests + + scramble_mac = 0; + + memcpy(scramble_mac_buf, s->mac, s->mlen); + + if (s->mlen > 0) { + scramble_mac = 1; + if (s->mlen < ETHER_ADDR_LEN + ETHER_VLAN_LEN) { + fprintf(stderr, + "scramble_init(): mac string is too short (%d)\n", + s->mlen); + return -1; + } + } + memcpy(scramble_ether_addr, scramble_mac_buf, ETHER_ADDR_LEN); + + /* we don't want to map ether unicast to multicast and visa versa */ + RESET_ETHER_MCAST(scramble_ether_addr); + + memcpy(&scramble_ether_vlan, scramble_mac_buf + ETHER_ADDR_LEN, ETHER_VLAN_LEN); + return 0; +} + +/* init everything from file, if it doesn't exist, create it */ +int +scramble_init_from_file(const char *fn, scramble_crypt_t c4, scramble_crypt_t c6, int *do_mac) +{ + // OSSL_PROVIDER *legacy_, *default_; + u_char pad[MAX_BLK_LENGTH]; + u_char key[MAX_BLK_LENGTH]; + u_char mac[MAX_BLK_LENGTH]; + u_char iv[MAX_BLK_LENGTH]; + + scramble_state_t s; + FILE *f; + + s.pad = pad; + s.key = key; + s.mac = mac; + s.iv = iv; + if ((f = fopen(fn, "r")) == NULL) { + if (errno != ENOENT) { + perror("scamble_init_file(): fopen"); + return -1; + } + if (c4 == SCRAMBLE_NONE || c6 == SCRAMBLE_NONE) + return -1; + + /* file doesn't exist, create it */ + s.c4 = c4; + s.c6 = c6; + s.plen = MAX_BLK_LENGTH; + s.klen = 16; /* XXX */ + s.ivlen = 16; + + if (scramble_newpad(pad, s.plen) < 0) + return -1; + if (scramble_newkey(key, s.klen) < 0) + return -1; + if (scramble_newiv(iv, s.ivlen) < 0) + return -1; + if (do_mac && *do_mac) { + s.mlen = ETHER_ADDR_LEN + ETHER_VLAN_LEN; + if (scramble_newmac(mac, s.mlen) < 0) + return -1; + } else + s.mlen = 0; + if (scramble_savestate(fn, &s) < 0) + return -1; + } else { + fclose(f); + s.plen = MAX_BLK_LENGTH; + s.klen = MAX_BLK_LENGTH; + s.mlen = MAX_BLK_LENGTH; + s.ivlen = MAX_BLK_LENGTH; + if (scramble_readstate(fn, &s) < 0) + return -1; + if (do_mac) + *do_mac = (s.mlen > 0); + } + + + if (scramble_init(&s) < 0) + return -1; + return 0; +} + +/* scramble IPv4 addresses, input and output are in network byte order */ +uint32_t +scramble_ip4(uint32_t input, int pass_bits) { + uint32_t output = 0; + uint32_t m = 0xffffffff << 1; + int i = 31; + int class_bits = 0; + int pbits = 0; + int outlen; +#define MAX_CLASS_BITS 4 + static int _class_bits[1<<MAX_CLASS_BITS] = { + 1,1,1,1,1,1,1,1, /* class A: preserve 1 bit */ + 2,2,2,2, /* class B: preserve 2 bits */ + 3,3, /* class C: preserve 3 bits */ + 4, /* class D: preserve 4 bits */ + 32 /* class bad, preserve all */ + }; + uint32_t *cp; + + input = ntohl(input); + cp = ip4cache + (input >> (32 - CACHE_BITS)); + + assert(pass_bits >= 0 && pass_bits < 33); + + ++ipv4_anon_calls; + + b4_in.ip4 = input; + + class_bits = _class_bits[input >> (32-MAX_CLASS_BITS)]; + + // check cache first + output = *cp; + if (output != 0) { + output <<= (32 - CACHE_BITS); + if (class_bits < CACHE_BITS) + class_bits = CACHE_BITS; + ++ipv4_cache_hits; + } + + pbits = MAX(pass_bits, class_bits); + + for (i = 31; i > pbits - 1; --i) { + /* pass through 'i' highest bits of ip4 */ + b4_in.ip4 &= m; + /* the following could be: + * b4_in.ip4 |= (ip4pad & ~m); */ + b4_in.ip4 |= (ip4pad >> i); + b4_in.ip4 = cryptopant_swap32(b4_in.ip4); + uint mdlen = MD5_DIGEST_LENGTH; + switch (scramble_crypto4) { + case SCRAMBLE_BLOWFISH: + if (!EVP_CipherUpdate(ctx4, (u_char*)&b4_out, &outlen, (u_char*)&b4_in, BF_BLOCK)) { + /* Error */ + fprintf(stderr, "scramble_ip4(): EVP_CipherUpdate failed"); + abort(); + } + case SCRAMBLE_AES: + if (!EVP_CipherUpdate(ctx4, (u_char*)&b4_out, &outlen, (u_char*)&b4_in, AES_BLOCK_SIZE)) { + /* Error */ + fprintf(stderr, "scramble_ip4(): EVP_CipherUpdate failed"); + abort(); + } + break; + case SCRAMBLE_SHA1: + mdlen = SHA_DIGEST_LENGTH; + // fallthrough + case SCRAMBLE_MD5: + if (!EVP_DigestInit_ex2(mdctx4, md4, NULL) || + !EVP_DigestUpdate(mdctx4, (u_char*)&b4_in, mdlen) || + !EVP_DigestFinal_ex(mdctx4, (u_char*)&b4_out, &mdlen)) { + /* Error */ + fprintf(stderr, "scramble_ip4(): EVP_Digest* failed"); + abort(); + } + break; + default: + abort(); + } + output |= (( *((u_char*)&b4_out.ip4) & 1) << (31 - i)); + b4_in.ip4 = cryptopant_swap32(b4_in.ip4); + m <<= 1; + } + + /* output == 0 is OK, means pass address unchanged */ + + *cp = (output >> (32 - CACHE_BITS)); + + return htonl(output ^ input); +} + +/* scramble ipv6 address in place, in network byte order */ +void +scramble_ip6(struct in6_addr *input, int pass_bits) +{ + struct in6_addr output; + int i, w; + int pbits = pass_bits; + + ++ipv6_anon_calls; + b6_in.ip6.s6_addr32[0] = ip6pad[0]; /* XXX this one not needed */ + b6_in.ip6.s6_addr32[1] = ip6pad[1]; + b6_in.ip6.s6_addr32[2] = ip6pad[2]; + b6_in.ip6.s6_addr32[3] = ip6pad[3]; + int outlen = AES_BLOCK_SIZE; + uint mdlen; + + for (w = 0; w < 4; ++w) { + uint32_t m = 0xffffffff << 1; + uint32_t x = ntohl(input->s6_addr32[w]); + uint32_t hpad = ntohl(ip6pad[w]); + output.s6_addr32[w] = 0; + /* anonymize x, using hpad */ + for (i = 31; i > pbits - 1; --i) { + /* pass through 'i' highest bits of the word */ + x &= m; + /* the following could be: + * x |= (hpad & ~m); */ + x |= (hpad >> i); + b6_in.ip6.s6_addr32[w] = htonl(x); + /* hashing proper */ + switch (scramble_crypto6) { + case SCRAMBLE_BLOWFISH: + /* use BF in chain mode */ + EVP_CIPHER_CTX_reset(ctx6); + if (!EVP_EncryptInit_ex2(ctx6, cipher6, scramble_key, ivec, NULL)) { + fprintf(stderr, + "scramble_init(): EVP_EncryptInit_ex2 failed:"); + ERR_print_errors_fp(stderr); + } + if (!EVP_CipherUpdate(ctx6, (u_char*)&b6_out, &outlen, (u_char*)&b6_in, sizeof(b6_in))) { + /* Error */ + fprintf(stderr, "scramble_ip6(): EVP_CipherUpdate failed\n"); + ERR_print_errors_fp(stderr); + } + break; + case SCRAMBLE_AES: + if (!EVP_CipherUpdate(ctx6, (u_char*)&b6_out, &outlen, (u_char*)&b6_in, AES_BLOCK_SIZE)) { + /* Error */ + fprintf(stderr, "scramble_ip6(): EVP_CipherUpdate failed"); + } + break; + case SCRAMBLE_SHA1: + case SCRAMBLE_MD5: + if (scramble_crypto6 == SCRAMBLE_SHA1) { + mdlen = SHA_DIGEST_LENGTH; + } else { + mdlen = MD5_DIGEST_LENGTH; + } + // fallthrough + if (!EVP_DigestInit_ex2(mdctx6, md6, NULL) || + !EVP_DigestUpdate(mdctx6, (u_char*)&b6_in, mdlen) || + !EVP_DigestFinal_ex(mdctx6, (u_char*)&b6_out, &mdlen)) { + /* Error */ + fprintf(stderr, "scramble_ip6(): EVP_Digest* failed"); + } + break; + default: + abort(); + } + output.s6_addr32[w] |= ((ntohl(b6_out.ip6.s6_addr32[3]) & 1) + << (31 - i)); + m <<= 1; + } + pbits = (pbits >= 32) ? pbits - 32 : 0; + /* pbits >= 32 this means the above for-loop wasn't executed */ + + output.s6_addr32[w] = htonl(output.s6_addr32[w]) ^ input->s6_addr32[w]; + + /* restore the word */ + b6_in.ip6.s6_addr32[w] = input->s6_addr32[w]; + } + *input = output; +} + +/* reverse map scrambled IP addresses, all network byte order */ +uint32_t +unscramble_ip4(uint32_t input, int pass_bits) +{ + int i; + uint32_t guess, res; + + guess = input; /* Starting with the input seems + * a good idea because some bits + * may be passed through + * unchanged */ + for (i=32; i>0; --i) { + res = scramble_ip4(guess, pass_bits); + /* we're only interested in flipping the + * higher bit, don't care about the rest */ + res ^= input; + if (res == 0) + return guess; + guess ^= res; + } + //unreachable, since there should be always a match + //(since we're zeroing out at least one bit per iteration) + assert(0); + return (0xffffffff); /* cannot find the match */ +} + +/* unscramble ipv6 address in place, in network byte order */ +void +unscramble_ip6(struct in6_addr *input, int pass_bits) +{ + struct in6_addr guess; + struct in6_addr res; + uint32_t r = 0; + + int i; + + guess = *input; + for (i = 0; i < 4; ++i) { + for (;;) { + res = guess; + scramble_ip6(&res, pass_bits); + r = res.s6_addr32[i] ^ input->s6_addr32[i]; + + if (r == 0) break; + + guess.s6_addr32[i] ^= r; + } + + } + *input = guess; + return; +} diff --git a/src/cryptopANT.h b/src/cryptopANT.h new file mode 100644 index 0000000..8f596ff --- /dev/null +++ b/src/cryptopANT.h @@ -0,0 +1,92 @@ +/* -*- Mode:C; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ +/* + * Copyright (C) 2004-2024 by the University of Southern California + * $Id: c23494984e1ae44af55668feda8232282c9473d0 $ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _SCRAMBLE_CRYPT_H +#define _SCRAMBLE_CRYPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define ETHER_ADDR_LEN 6 +#define ETHER_VLAN_LEN 2 + +#define _XOR16(a, b, i) (((uint16_t *)(a))[i] ^= ((uint16_t *)(b))[i]) +#define _XOR32(a, b, i) (((uint32_t *)(a))[i] ^= ((uint32_t *)(b))[i]) + +#define SCRAMBLE_ETHER_ADDR(a) if (1) { \ + _XOR32(a, scramble_ether_addr, 0); \ + _XOR16(a, scramble_ether_addr, 2); \ + } + +#define SCRAMBLE_ETHER_VLAN(v) ((v) ^= scramble_ether_vlan); + +#define SCRAMBLE_RANDOM_DEV "/dev/urandom" + +typedef enum { + SCRAMBLE_NONE = 0x00, + SCRAMBLE_MD5 = 0x01, + SCRAMBLE_BLOWFISH = 0x02, + SCRAMBLE_AES = 0x03, + SCRAMBLE_SHA1 = 0x04 +} scramble_crypt_t; + +typedef struct { + scramble_crypt_t c4; + scramble_crypt_t c6; + u_char *key; + int klen; + u_char *pad; + int plen; + u_char *mac; + int mlen; + u_char *iv; + int ivlen; +} scramble_state_t; + +/* external vars exported by mac scrambling macros */ +extern uint8_t scramble_ether_addr[ETHER_ADDR_LEN]; +extern uint16_t scramble_ether_vlan; +extern int scramble_mac; /* 0/1 */ + +/* public functions */ +extern scramble_crypt_t scramble_crypto_ip4 (void); +extern scramble_crypt_t scramble_crypto_ip6 (void); +extern scramble_crypt_t scramble_name2type (const char *); +extern const char* scramble_type2name (scramble_crypt_t); +extern int scramble_newkey (u_char *, int); +extern int scramble_newpad (u_char *, int); +extern int scramble_newmac (u_char *, int); +extern int scramble_readstate (const char *, scramble_state_t *); +extern int scramble_savestate (const char *, const scramble_state_t *); +extern int scramble_init (const scramble_state_t *s); +extern int scramble_init_from_file (const char *, scramble_crypt_t, scramble_crypt_t, int *); +extern void scramble_cleanup (); +extern uint32_t scramble_ip4 (uint32_t, int); +extern uint32_t unscramble_ip4 (uint32_t, int); +extern void scramble_ip6 (struct in6_addr *, int); +extern void unscramble_ip6 (struct in6_addr *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _SCRAMBLE_CRYPT_H */ diff --git a/src/scramble_ips.c b/src/scramble_ips.c new file mode 100644 index 0000000..5222301 --- /dev/null +++ b/src/scramble_ips.c @@ -0,0 +1,487 @@ +/* -*- Mode:C; c-basic-offset:4; tab-width:8; indent-tabs-mode:t -*- */ +/* + * Copyright (C) 2004-2024 by the University of Southern California + * $Id: a91d5228a362749a1ccff71f0fbdc092ac7ebdcc $ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program 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 this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* setup environment */ +#define _GNU_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + +#include <stdio.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <getopt.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <regex.h> //xxx need to check in autoconf +#include <assert.h> +#include "cryptopANT.h" + +//autoconfigured +#include "config.h" + +//long options +#define PASS4 256 +#define PASS6 257 +#define KTYPE 258 +#define IPPREFIX 259 +#define IPSUFFIX 260 + +#define IP4MAXLEN 15 //strlen("xxx.xxx.xxx.xxx") +#define IP6MAXLEN 39 //strlen("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx") +#define CBUF_SZ (IP6MAXLEN*2) + +#define INITBUFSIZE 4096 //xxx need more space in case text expands due to IP scrambling + +#if HAVE__U6_ADDR32 +#define s6_addr32 __u6_addr.__u6_addr32 +#endif + +#if HAVE_REGEX_H +static const char REGEX4[]="((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))"; +static const char REGEX6[]="(((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?)"; +#endif //HAVE_REGEX_H + +static int pass_bits4 = 0; +static int pass_bits6 = 0; +static int reverse_mode = 0; + +//buffers for in/out processing +static char *linebuf1 = NULL,*linebuf2 = NULL; +static size_t linebuf1_sz = 0, linebuf2_sz = 0; + +static char cbuf[CBUF_SZ]; + +void +usage(const char *pname) { + fprintf(stderr, + "Read IP addresses from stdin and print scrambled addresses on stdout\n" + "USAGE:\n" + "\t%s [OPTIONS] [-r] key_file\n" + "\tWhere OPTIONS are:\n" + "\t\t--newkey|-G generate a new key key_file\n" + "\t\t--type=TYPE crypto to use for key generation (valid only with --newkey option)\n" + "\t\t supported choices are: blowfish (default), aes, sha1, md5\n" + "\t\t--pass4=<num> pass <num> higher bits of ipv4 addresses through unchanged\n" + "\t\t--pass6=<num> pass <num> higher bits of ipv6 addresses through unchanged\n" +#if HAVE_REGEX_H + "\t\t--ip-prefix=<regex> all ips must be prefixed by this regex prefix (both ipv4 and ipv6)\n" + "\t\t--ip-suffix=<regex> all ips must be followed by this regex suffix (both ipv4 and ipv6)\n" + "\t\t-t text mode: read text from stdin and scramble all addresses\n" + "\t\t that can be found using regex (use with caution)\n" +#endif //HAVE_REGEX_H + "\t\t-r reverse-mode i.e. for unscrambling ip addresses\n" + , pname); + exit(1); +} + +int +anon_ip4_txt(const char *oldip, char *newip) { + struct in_addr ip4, ip4s; + if (inet_pton(AF_INET, oldip, &ip4) <= 0) { + fprintf(stderr, "don't understand address (%s)\n", oldip); +#ifdef HAVE_STRLCPY + strlcpy(newip, oldip, INITBUFSIZE); +#else + strcpy(newip, oldip); //copy without changing +#endif + } else { + ip4s.s_addr = (reverse_mode) + ? unscramble_ip4(ip4.s_addr, pass_bits4) + : scramble_ip4(ip4.s_addr, pass_bits4); + if (newip != inet_ntop(AF_INET, &ip4s, newip, 256)) { + perror("Error: can't print new address"); + exit(1); + } + } + return strlen(newip); +} +int +anon_ip6_txt(const char *oldip, char *newip) { + struct in6_addr ip6; + if (inet_pton(AF_INET6, oldip, &ip6) <= 0) { + fprintf(stderr, "don't understand address (%s)\n", oldip); +#ifdef HAVE_STRLCPY + strlcpy(newip, oldip, INITBUFSIZE); +#else + strcpy(newip, oldip); //copy without changing +#endif + } else { + if (reverse_mode) { + unscramble_ip6(&ip6, pass_bits6); + } else { + scramble_ip6(&ip6, pass_bits6); + } + if (newip != inet_ntop(AF_INET6, &ip6, newip, 256)) { + perror("Error: can't print new address"); + exit(1); + } + } + return strlen(newip); +} + +//return the pointer in 'in' buffer where processing stopped +const char* +search_replace_ip(const char *in, char *out, const regex_t *r, + int (*anonf)(const char*, char*), size_t *outsz) { + const char *c = in; + char *c2 = out; + regmatch_t re_match[2]; + //0th element is the whole thing (including prefix and suffix) + //1st element is the IP address + while (0 == regexec(r, c, 2, re_match, 0)) { + const char *cc=c; + if (re_match[1].rm_so != -1) { + const char *ipbeg = cc + re_match[1].rm_so; + const char *ipend = cc + re_match[1].rm_eo; + size_t copylen = ipbeg - c; //from prev match to this one, pass-thru, including prefix + //copy from c..(beg-1), advance both pointers + while (c2 - out + copylen >= *outsz) { + *outsz *= 2; + out = realloc(out, *outsz); + if (out == NULL) { + fprintf(stderr, "Error: run out of buffer space\n"); + exit(1); + } + } + memcpy(c2, c, copylen); + c += copylen; + c2+= copylen; + char hold = *ipend; //store last character, we'll need temporarly replace it with '\0' + *(char*)ipend = '\0'; + size_t anoniplen = anonf(c, c2); + *(char*)ipend = hold; //restore + c = ipend; + c2 += anoniplen; + } + } + //copy the rest + size_t copylen = strlen(c) + 1; + while (c2 - out + copylen >= *outsz) { + *outsz *= 2; + out = realloc(out, *outsz); + if (out == NULL) { + fprintf(stderr, "Error: run out of buffer space\n"); + exit(1); + } + } + memcpy(c2, c, copylen); + + return out; +} + +int +main(int argc, char *argv[]) +{ + FILE *keyfile = NULL; + const char *keyfn = NULL; + const char *pname = argv[0]; + + int opt; + int text_mode = 0; + int opt_newkey = 0; + char *opt_keytype = NULL; +#if HAVE_REGEX_H + char *opt_ipprefix = NULL; + char *opt_ipsuffix = NULL; + char regex4[4096]; + char regex6[4096]; +#endif //HAVE_REGEX_H + + scramble_crypt_t key_crypto = SCRAMBLE_BLOWFISH; + + struct option long_options[] = { + {"newkey",0, NULL, 'G'}, + {"help", 0, NULL, 'h'}, + {"pass4", 1, NULL, PASS4}, + {"pass6", 1, NULL, PASS6}, + {"type", 1, NULL, KTYPE}, +#if HAVE_REGEX_H + {"text", 0, NULL, 't'}, + {"ip-prefix", 1, NULL, IPPREFIX}, + {"ip-suffix", 1, NULL, IPSUFFIX}, +#endif //HAVE_REGEX_H + }; + + while((opt = getopt_long(argc, argv, + "Ghr" +#if HAVE_REGEX_H + "t" +#endif //HAVE_REGEX_H + , long_options, NULL)) != EOF) { + switch(opt) { + /* long options first: */ + case PASS4: + pass_bits4 = atoi(optarg); + if (pass_bits4 < 0 || pass_bits4 > 32) { + fprintf(stderr, "Error: --pass4 option argument must be within [0..32]\n"); + exit(1); + } + break; + case PASS6: + pass_bits6 = atoi(optarg); + if (pass_bits6 < 0 || pass_bits6 > 128) { + fprintf(stderr, "Error: --pass6 option argument must be within [0..128]\n"); + exit(1); + } + break; + case KTYPE: + opt_keytype = optarg; + if (strcmp(opt_keytype, "blowfish") == 0) { + key_crypto = SCRAMBLE_BLOWFISH; + } else if (strcmp(opt_keytype, "aes") == 0) { + key_crypto = SCRAMBLE_AES; + } else if (strcmp(opt_keytype, "sha1") == 0) { + key_crypto = SCRAMBLE_SHA1; + } else if (strcmp(opt_keytype, "md5") == 0) { + key_crypto = SCRAMBLE_MD5; + } else { + fprintf(stderr, "Error: unsupported crypto key type: '%s' (can be one of: blowfish, aes, sha1, md5)\n", opt_keytype); + exit(1); + } + break; +#if HAVE_REGEX_H + case IPPREFIX: + opt_ipprefix = optarg; + break; + case IPSUFFIX: + opt_ipsuffix = optarg; + break; + case 't': + text_mode = 1; + break; +#endif //HAVE_REGEX_H + + /* short options: */ + case 'G': + opt_newkey = 1; + break; + case 'h': + usage(pname); + /* never returns */ + break; + case 'r': + reverse_mode = 1; + break; + + default: + usage(pname); + } + } + argc -= optind; + argv += optind; + if (argc != 1) { + usage(pname); + } + + keyfn = argv[0]; + + if (opt_newkey && (text_mode || reverse_mode || pass_bits4 || pass_bits6)) { + fprintf(stderr, "Error: --newkey or -G is mutually exclusive with other options.\n"); + exit(1); + } + if (opt_keytype && !opt_newkey) { + fprintf(stderr, "Error: --type requires --newkey (-G) option.\n"); + exit(1); + } +#if HAVE_REGEX_H + if (!text_mode && (opt_ipprefix != NULL || opt_ipsuffix != NULL)) { + fprintf(stderr, "--ip-prefix and --ipsuffix require text mode (-t).\n"); + exit(1); + } +#endif //HAVE_REGEX_H + if ((keyfile = fopen(keyfn, "r")) == NULL) { + if (!opt_newkey) { + /* no keyfile, but supposed to exist */ + fprintf(stderr, "Error: cannot open the key_file: '%s': %s\n", keyfn, strerror(errno)); + exit(1); + } + } else { + if (opt_newkey) { + /* keyfile exists, but asked to make new one */ + fprintf(stderr, "Error: keyfile '%s' already exists, remove it before trying to generate a new one.\n", + keyfn); + exit(1); + } + } + if (keyfile) fclose(keyfile); + + if (scramble_init_from_file(keyfn, key_crypto, key_crypto, NULL) < 0) { + fprintf(stderr, "Error: can't initialize from keyfile '%s'\n", keyfn); + exit(1); + } + if (opt_newkey) { + //only generating mode + exit(0); + } + + if (!text_mode && setvbuf(stdout, NULL, _IOLBF, 0) < 0) { + fprintf(stderr, "Error: setting line buffering: %s\n", strerror(errno)); + exit(1); + } + +#if 1 //HAVE_REGEX_H + if (text_mode) { + linebuf1_sz = INITBUFSIZE; + linebuf2_sz = INITBUFSIZE; + linebuf1 = malloc(linebuf1_sz); + linebuf2 = malloc(linebuf2_sz); + + memset(regex4, 0, sizeof(regex4)); + memset(regex6, 0, sizeof(regex6)); + if (opt_ipprefix != NULL) { + strncat(regex4, opt_ipprefix, sizeof(regex4)-1); + strncat(regex6, opt_ipprefix, sizeof(regex6)-1); + } + strncat(regex4, REGEX4, sizeof(regex4)-1); + strncat(regex6, REGEX6, sizeof(regex6)-1); + if (opt_ipsuffix != NULL) { + strncat(regex4, opt_ipsuffix, sizeof(regex4)-1); + strncat(regex6, opt_ipsuffix, sizeof(regex6)-1); + } + if (regex4[sizeof(regex4)-1] != '\0' || + regex6[sizeof(regex6)-1] != '\0') { + fprintf(stderr, "Error: regex for ip addresses is too long."); + exit(1); + } + + regex_t r4, r6; + if (0 != regcomp(&r4, regex4, REG_EXTENDED|REG_NEWLINE)) + exit(1); + if (0 != regcomp(&r6, regex6, REG_EXTENDED|REG_NEWLINE)) + exit(1); + linebuf1[linebuf1_sz-1]='x'; //anything but zero + //xxx todo: use buffered reading/writing + while (fgets(linebuf1, linebuf1_sz, stdin) != NULL) { + //fgets adds a terminating '\0', reading stops after an EOF or a newline + if (linebuf1[linebuf1_sz-1] == '\0' && linebuf1[linebuf1_sz-2] != '\n') { + //filled the buffer, but no newline; xxx remove newline dependency + fprintf(stderr, "Error: input lines are too long; maximum supported is %zd.\n", + linebuf1_sz-1); + exit(1); + } + //read a full line, can be anonymized in full + linebuf2 = (char*)search_replace_ip(linebuf1, linebuf2, &r4, &anon_ip4_txt, &linebuf2_sz); + linebuf1 = (char*)search_replace_ip(linebuf2, linebuf1, &r6, &anon_ip6_txt, &linebuf1_sz); + //output + fputs(linebuf1, stdout); + } + regfree(&r4); + regfree(&r6); + free(linebuf1); + free(linebuf2); + return 0; + } +#endif + + for (;;) { + int af; + int i; + int prefix = 0; + char *plen = NULL; + char *c; + + struct in_addr ip4, ip4s; + struct in6_addr ip6, ip6s; + void *new = NULL; + // char *c2; + if (fgets(cbuf, CBUF_SZ, stdin) == NULL) + break; + for (i = strnlen(cbuf, CBUF_SZ-1)-1; i >= 0; --i) { + if (!isgraph(cbuf[i])) + cbuf[i] = '\0'; + } + plen = NULL; + /* first see if this is a network */ + if (NULL != (c = strchr(cbuf, '/'))) { + /* this is a network */ + plen = c + 1; + if (!isdigit(*plen)) { + fprintf(stderr, "can't parse network prefix (%s)\n", cbuf); + continue; + } + *c = '\0'; + prefix = atoi(plen); + } + /* first try ipv4 */ + af= AF_INET; + if (inet_pton(af, cbuf, &ip4) <= 0) { + /* next try ipv6 */ + af= AF_INET6; + if (inet_pton(af, cbuf, &ip6) <= 0) { + fprintf(stderr, "don't understand address (%s)\n", cbuf); + continue; + } + ip6s = ip6; + if (reverse_mode) { + unscramble_ip6(&ip6s, pass_bits6); + } else { + scramble_ip6(&ip6s, pass_bits6); + } + new = &ip6s; + /* if it was a network, zero out host bits */ + if (plen && prefix < 128) { + int hostbits = 128 - prefix; + i = 3; + while (hostbits >= 32) { + ip6s.s6_addr32[i] = 0; + --i; + hostbits -= 32; + } + if (hostbits > 0) { + ip6s.s6_addr32[i] = htonl(ntohl(ip6s.s6_addr32[i]) + & (0xffffffffUL << hostbits)); + } + } + } else { + ip4s.s_addr = (reverse_mode) + ? unscramble_ip4(ip4.s_addr, pass_bits4) + : scramble_ip4(ip4.s_addr, pass_bits4); + new = &ip4s; + /* if it was a network, zero out host bits */ + if (plen && prefix < 32) { + int hostbits = 32 - prefix; + ip4s.s_addr = ntohl(ip4s.s_addr); + if (hostbits == 32) + ip4s.s_addr = 0; + else + ip4s.s_addr &= 0xffffffffUL << hostbits; + ip4s.s_addr = htonl(ip4s.s_addr); + } + } + + if (cbuf != inet_ntop(af, new, cbuf, CBUF_SZ)) { + perror("Error: can't print new address"); + exit(1); + } + + printf("%s", cbuf); + if (plen) + printf("/%d", prefix); + printf("\n"); + } + scramble_cleanup(); + return 0; +} |