Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

944 lines (827 sloc) 22.163 kb
/* zcrypt.c -- Read in a data stream from stdin & dump a decrypted/encrypted *
* datastream. Reads the string to make the key from from the first *
* parameter. Encrypts or decrypts according to -d or -e flag. (-e is *
* default.) Will invoke zwrite if the -c option is provided for *
* encryption. If a zephyr class is specified & the keyfile name omitted *
* the ~/.crypt-table will be checked for "crypt-classname" and then *
* "crypt-default" for the keyfile name. */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <glib.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <ctype.h>
#include <limits.h>
#include <getopt.h>
#include <config.h>
#ifdef HAVE_KERBEROS_IV
#include <kerberosIV/des.h>
#else
#include <openssl/des.h>
#endif
#include "filterproc.h"
extern const char *version;
/* Annotate functions in which the caller owns the return value and is
* responsible for ensuring it is freed. */
#define CALLER_OWN G_GNUC_WARN_UNUSED_RESULT
#define MAX_KEY 128
#define MAX_LINE 128
#define MAX_RESULT 4096
#ifndef TRUE
#define TRUE -1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define ZWRITE_OPT_NOAUTH (1<<0)
#define ZWRITE_OPT_SIGNATURE (1<<1)
#define ZWRITE_OPT_IGNOREVARS (1<<2)
#define ZWRITE_OPT_VERBOSE (1<<3)
#define ZWRITE_OPT_QUIET (1<<4)
#define ZCRYPT_OPT_MESSAGE (1<<5)
#define ZCRYPT_OPT_IGNOREDOT (1<<6)
typedef struct
{
int flags;
const char *signature;
char *message;
} ZWRITEOPTIONS;
CALLER_OWN char *GetZephyrVarKeyFile(const char *whoami, const char *class, const char *instance);
int ParseCryptSpec(const char *spec, const char **keyfile);
CALLER_OWN char *BuildArgString(char **argv, int start, int end);
CALLER_OWN char *read_keystring(const char *keyfile);
int do_encrypt(int zephyr, const char *class, const char *instance,
ZWRITEOPTIONS *zoptions, const char* keyfile, int cipher);
int do_encrypt_des(const char *keyfile, const char *in, int len, FILE *out);
int do_encrypt_aes(const char *keyfile, const char *in, int len, FILE *out);
int do_decrypt(const char *keyfile, int cipher);
int do_decrypt_aes(const char *keyfile);
int do_decrypt_des(const char *keyfile);
#define M_NONE 0
#define M_ZEPHYR_ENCRYPT 1
#define M_DECRYPT 2
#define M_ENCRYPT 3
#define M_RANDOMIZE 4
#define M_SETKEY 5
enum cipher_algo {
CIPHER_DES,
CIPHER_AES,
NCIPHER
};
typedef struct {
int (*encrypt)(const char *keyfile, const char *in, int len, FILE *out);
int (*decrypt)(const char *keyfile);
} cipher_pair;
cipher_pair ciphers[NCIPHER] = {
[CIPHER_DES] = { do_encrypt_des, do_decrypt_des},
[CIPHER_AES] = { do_encrypt_aes, do_decrypt_aes},
};
static void owl_zcrypt_string_to_schedule(char *keystring, des_key_schedule *schedule) {
#ifdef HAVE_KERBEROS_IV
des_cblock key;
#else
des_cblock _key, *key = &_key;
#endif
des_string_to_key(keystring, key);
des_key_sched(key, *schedule);
}
void usage(FILE *file, const char *progname)
{
fprintf(file, "Usage: %s [-Z|-D|-E|-R|-S] [-F Keyfile] [-c class] [-i instance]\n", progname);
fprintf(file, " [-advqtluon] [-s signature] [-f arg] [-m message]\n");
fprintf(file, " One or more of class, instance, and keyfile must be specified.\n");
}
int main(int argc, char *argv[])
{
char *cryptspec = NULL;
const char *keyfile;
int cipher;
int error = FALSE;
int zephyr = FALSE;
const char *class = NULL, *instance = NULL;
int mode = M_NONE;
int c;
int messageflag = FALSE;
ZWRITEOPTIONS zoptions;
zoptions.flags = 0;
enum {
OPT_VERSION = CHAR_MAX + 1,
OPT_HELP,
};
static const struct option options[] = {
{"version", no_argument, NULL, OPT_VERSION},
{"help", no_argument, NULL, OPT_HELP},
{NULL, 0, NULL, 0}
};
while ((c = getopt_long(argc, argv, "ZDERSF:c:i:advqtluons:f:m", options, NULL)) != -1)
{
switch(c)
{
case OPT_VERSION:
/* Version */
printf("This is zcrypt version %s\n", version);
exit(0);
case OPT_HELP:
/* Help */
usage(stdout, argv[0]);
exit(0);
case 'Z':
/* Zephyr encrypt */
mode = M_ZEPHYR_ENCRYPT;
break;
case 'D':
/* Decrypt */
mode = M_DECRYPT;
break;
case 'E':
/* Encrypt */
mode = M_ENCRYPT;
break;
case 'R':
/* Randomize the keyfile */
mode = M_RANDOMIZE;
break;
case 'S':
/* Set a new key value from stdin */
mode = M_SETKEY;
break;
case 'F':
/* Specify the keyfile explicitly */
if (cryptspec != NULL) error = TRUE;
cryptspec = optarg;
break;
case 'c':
/* Zwrite/zcrypt: class name */
if (class != NULL) error = TRUE;
class = optarg;
break;
case 'i':
/* Zwrite/zcrypt: instance name */
if (instance != NULL) error = TRUE;
instance = optarg;
break;
case 'a':
/* Zwrite: authenticate (default) */
zoptions.flags &= ~ZWRITE_OPT_NOAUTH;
break;
case 'd':
/* Zwrite: do not authenticate */
zoptions.flags |= ZWRITE_OPT_NOAUTH;
break;
case 'v':
/* Zwrite: verbose */
zoptions.flags |= ZWRITE_OPT_VERBOSE;
break;
case 'q':
/* Zwrite: quiet */
zoptions.flags |= ZWRITE_OPT_QUIET;
break;
case 't':
/* Zwrite: no expand tabs (ignored) */
break;
case 'l':
/* Zwrite: ignore '.' on a line by itself (ignored) */
zoptions.flags |= ZCRYPT_OPT_IGNOREDOT;
break;
case 'u':
/* Zwrite: urgent message */
instance = "URGENT";
break;
case 'o':
/* Zwrite: ignore zephyr variables zwrite-class, zwrite-inst, */
/* zwrite-opcode */
zoptions.flags |= ZWRITE_OPT_IGNOREVARS;
break;
case 'n':
/* Zwrite: prevent PING message (always used) */
break;
case 's':
/* Zwrite: signature */
zoptions.flags |= ZWRITE_OPT_SIGNATURE;
zoptions.signature = optarg;
break;
case 'f':
/* Zwrite: file system specification (ignored) */
break;
case 'm':
/* Message on rest of line*/
messageflag = TRUE;
break;
case '?':
error = TRUE;
break;
}
if (error || messageflag)
break;
}
if (class != NULL || instance != NULL)
zephyr = TRUE;
if (messageflag)
{
zoptions.flags |= ZCRYPT_OPT_MESSAGE;
zoptions.message = BuildArgString(argv, optind, argc);
if (!zoptions.message)
{
fprintf(stderr, "Memory allocation error.\n");
error = TRUE;
}
}
else if (optind < argc)
{
error = TRUE;
}
if (mode == M_NONE)
mode = (zephyr?M_ZEPHYR_ENCRYPT:M_ENCRYPT);
if (mode == M_ZEPHYR_ENCRYPT && !zephyr)
error = TRUE;
if (!error && cryptspec == NULL && (class != NULL || instance != NULL)) {
cryptspec = GetZephyrVarKeyFile(argv[0], class, instance);
if(!cryptspec) {
fprintf(stderr, "Unable to find keyfile for ");
if(class != NULL) {
fprintf(stderr, "-c %s ", class);
}
if(instance != NULL) {
fprintf(stderr, "-i %s ", instance);
}
fprintf(stderr, "\n");
exit(-1);
}
}
if (error || !cryptspec)
{
usage(stderr, argv[0]);
exit(1);
}
cipher = ParseCryptSpec(cryptspec, &keyfile);
if(cipher < 0) {
fprintf(stderr, "Invalid cipher specification: %s\n", cryptspec);
exit(1);
}
if (mode == M_RANDOMIZE)
{
/* Choose a new, random key */
/*
FILE *fkey = fopen(fname, "w");
if (!fkey)
printf("Could not open key file for writing: %s\n", fname);
else
{
char string[100];
fputs(fkey, string);
fclose(fkey);
}
*/
fprintf(stderr, "Feature not yet implemented.\n");
}
else if (mode == M_SETKEY)
{
/* Set a new, user-entered key */
char newkey[MAX_KEY];
FILE *fkey;
if (isatty(0))
{
printf("Enter new key: ");
/* Really should read without echo!!! */
}
if(!fgets(newkey, MAX_KEY - 1, stdin)) {
fprintf(stderr, "Error reading key.\n");
return 1;
}
fkey = fopen(keyfile, "w");
if (!fkey)
fprintf(stderr, "Could not open key file for writing: %s\n", keyfile);
else
{
if (fputs(newkey, fkey) != strlen(newkey) || putc('\n', fkey) != '\n')
{
fprintf(stderr, "Error writing to key file.\n");
fclose(fkey);
exit(1);
}
else
{
fclose(fkey);
fprintf(stderr, "Key update complete.\n");
}
}
}
else
{
if (mode == M_ZEPHYR_ENCRYPT || mode == M_ENCRYPT)
error = !do_encrypt((mode == M_ZEPHYR_ENCRYPT), class, instance,
&zoptions, keyfile, cipher);
else
error = !do_decrypt(keyfile, cipher);
}
/* Always print the **END** message if -D is specified. */
if (mode == M_DECRYPT)
printf("**END**\n");
return error;
}
int ParseCryptSpec(const char *spec, const char **keyfile) {
int cipher = CIPHER_DES;
char *cipher_name = strdup(spec);
char *colon = strchr(cipher_name, ':');
*keyfile = spec;
if (colon) {
char *rest = strchr(spec, ':') + 1;
while(isspace(*rest)) rest++;
*colon-- = '\0';
while (colon >= cipher_name && isspace(*colon)) {
*colon = '\0';
}
if(strcmp(cipher_name, "AES") == 0) {
cipher = CIPHER_AES;
*keyfile = rest;
} else if(strcmp(cipher_name, "DES") == 0) {
cipher = CIPHER_DES;
*keyfile = rest;
}
}
free(cipher_name);
return cipher;
}
/* Build a space-separated string from argv from elements between start *
* and end - 1. malloc()'s the returned string. */
CALLER_OWN char *BuildArgString(char **argv, int start, int end)
{
int len = 1;
int i;
char *result;
/* Compute the length of the string. (Plus 1 or 2) */
for (i = start; i < end; i++)
len += strlen(argv[i]) + 1;
/* Allocate memory */
result = (char *)malloc(len);
if (result)
{
/* Build the string */
char *ptr = result;
/* Start with an empty string, in case nothing is copied. */
*ptr = '\0';
/* Copy the arguments */
for (i = start; i < end; i++)
{
char *temp = argv[i];
/* Add a space, if not the first argument */
if (i != start)
*ptr++ = ' ';
/* Copy argv[i], leaving ptr pointing to the '\0' copied from temp */
while ((*ptr = *temp++))
ptr++;
}
}
return result;
}
#define MAX_BUFF 258
#define MAX_SEARCH 3
/* Find the class/instance in the .crypt-table */
CALLER_OWN char *GetZephyrVarKeyFile(const char *whoami, const char *class, const char *instance)
{
char *keyfile = NULL;
char *varname[MAX_SEARCH];
int length[MAX_SEARCH], i;
char buffer[MAX_BUFF];
const char *home;
char *filename;
char result[MAX_SEARCH][MAX_BUFF];
int numsearch = 0;
FILE *fsearch;
memset(varname, 0, sizeof(varname));
/* Determine names to look for in .crypt-table */
if (instance)
varname[numsearch++] = g_strdup_printf("crypt-%s-%s:", (class?class:"message"), instance);
if (class)
varname[numsearch++] = g_strdup_printf("crypt-%s:", class);
varname[numsearch++] = g_strdup("crypt-default:");
/* Setup the result array, and determine string lengths */
for (i = 0; i < numsearch; i++)
{
result[i][0] = '\0';
length[i] = strlen(varname[i]);
}
/* Open~/.crypt-table */
home = getenv("HOME");
if (home == NULL)
home = g_get_home_dir();
filename = g_build_filename(home, ".crypt-table", NULL);
fsearch = fopen(filename, "r");
if (fsearch)
{
/* Scan file for a match */
while (!feof(fsearch))
{
if (!fgets(buffer, MAX_BUFF - 3, fsearch)) break;
for (i = 0; i < numsearch; i++)
if (strncasecmp(varname[i], buffer, length[i]) == 0)
{
int j;
for (j = length[i]; buffer[j] == ' '; j++)
;
strcpy(result[i], &buffer[j]);
if (*result[i])
if (result[i][strlen(result[i])-1] == '\n')
result[i][strlen(result[i])-1] = '\0';
}
}
/* Pick the "best" match found */
keyfile = NULL;
for (i = 0; i < numsearch; i++)
if (*result[i])
{
keyfile = result[i];
break;
}
if (keyfile != NULL)
{
/* Prepare result to be returned */
char *temp = keyfile;
keyfile = (char *)malloc(strlen(temp) + 1);
if (keyfile)
strcpy(keyfile, temp);
else
fprintf(stderr, "Memory allocation error.\n");
}
fclose(fsearch);
}
else
fprintf(stderr, "Could not open key table file: %s\n", filename);
for(i = 0; i < MAX_SEARCH; i++) {
g_free(varname[i]);
}
g_free(filename);
return keyfile;
}
static pid_t zephyrpipe_pid = 0;
/* Open a pipe to zwrite */
FILE *GetZephyrPipe(const char *class, const char *instance, const ZWRITEOPTIONS *zoptions)
{
int fildes[2];
pid_t pid;
FILE *result;
const char *argv[20];
int argc = 0;
if (pipe(fildes) < 0)
return NULL;
pid = fork();
if (pid < 0)
{
/* Error: clean up */
close(fildes[0]);
close(fildes[1]);
result = NULL;
}
else if (pid == 0)
{
/* Setup child process */
argv[argc++] = "zwrite";
argv[argc++] = "-n"; /* Always send without ping */
if (class)
{
argv[argc++] = "-c";
argv[argc++] = class;
}
if (instance)
{
argv[argc++] = "-i";
argv[argc++] = instance;
}
if (zoptions->flags & ZWRITE_OPT_NOAUTH)
argv[argc++] = "-d";
if (zoptions->flags & ZWRITE_OPT_QUIET)
argv[argc++] = "-q";
if (zoptions->flags & ZWRITE_OPT_VERBOSE)
argv[argc++] = "-v";
if (zoptions->flags & ZWRITE_OPT_SIGNATURE)
{
argv[argc++] = "-s";
argv[argc++] = zoptions->signature;
}
argv[argc++] = "-O";
argv[argc++] = "crypt";
argv[argc] = NULL;
close(fildes[1]);
if (fildes[0] != STDIN_FILENO)
{
if (dup2(fildes[0], STDIN_FILENO) != STDIN_FILENO)
exit(0);
close(fildes[0]);
}
close(fildes[0]);
execvp(argv[0], (char **)argv);
fprintf(stderr, "Exec error: could not run zwrite\n");
exit(0);
}
else
{
close(fildes[0]);
/* Create a FILE * for the zwrite pipe */
result = (FILE *)fdopen(fildes[1], "w");
zephyrpipe_pid = pid;
}
return result;
}
/* Close the pipe to zwrite */
void CloseZephyrPipe(FILE *pipe)
{
fclose(pipe);
waitpid(zephyrpipe_pid, NULL, 0);
zephyrpipe_pid = 0;
}
#define BASE_CODE 70
#define LAST_CODE (BASE_CODE + 15)
#define OUTPUT_BLOCK_SIZE 16
void block_to_ascii(unsigned char *output, FILE *outfile)
{
int i;
for (i = 0; i < 8; i++)
{
putc(((output[i] & 0xf0) >> 4) + BASE_CODE, outfile);
putc( (output[i] & 0x0f) + BASE_CODE, outfile);
}
}
CALLER_OWN char *slurp_stdin(int ignoredot, int *length) {
char *buf;
char *inptr;
if ((inptr = buf = (char *)malloc(MAX_RESULT)) == NULL)
{
fprintf(stderr, "Memory allocation error\n");
return NULL;
}
while (inptr - buf < MAX_RESULT - MAX_LINE - 20)
{
if (fgets(inptr, MAX_LINE, stdin) == NULL)
break;
if (inptr[0])
{
if (inptr[0] == '.' && inptr[1] == '\n' && !ignoredot)
{
inptr[0] = '\0';
break;
}
else
inptr += strlen(inptr);
}
else
break;
}
*length = inptr - buf;
return buf;
}
CALLER_OWN char *GetInputBuffer(ZWRITEOPTIONS *zoptions, int *length) {
char *buf;
if (zoptions->flags & ZCRYPT_OPT_MESSAGE)
{
/* Use the -m message */
buf = strdup(zoptions->message);
*length = strlen(buf);
}
else
{
if (isatty(0)) {
/* tty input, so show the "Type your message now..." message */
if (zoptions->flags & ZCRYPT_OPT_IGNOREDOT)
printf("Type your message now. End with the end-of-file character.\n");
else
printf("Type your message now. End with control-D or a dot on a line by itself.\n");
} else {
zoptions->flags |= ZCRYPT_OPT_IGNOREDOT;
}
buf = slurp_stdin(zoptions->flags & ZCRYPT_OPT_IGNOREDOT, length);
}
return buf;
}
CALLER_OWN char *read_keystring(const char *keyfile) {
char *keystring;
FILE *fkey = fopen(keyfile, "r");
if(!fkey) {
fprintf(stderr, "Unable to open keyfile %s\n", keyfile);
return NULL;
}
keystring = malloc(MAX_KEY);
if(!fgets(keystring, MAX_KEY-1, fkey)) {
fprintf(stderr, "Unable to read from keyfile: %s\n", keyfile);
free(keystring);
keystring = NULL;
}
fclose(fkey);
return keystring;
}
/* Encrypt stdin, with prompt if isatty, and send to stdout, or to zwrite
if zephyr is set. */
int do_encrypt(int zephyr, const char *class, const char *instance,
ZWRITEOPTIONS *zoptions, const char *keyfile, int cipher)
{
FILE *outfile = stdout;
char *inbuff = NULL;
int buflen;
int out = TRUE;
inbuff = GetInputBuffer(zoptions, &buflen);
if(!inbuff) {
fprintf(stderr, "Error reading zcrypt input!\n");
return FALSE;
}
if (zephyr) {
outfile = GetZephyrPipe(class, instance, zoptions);
if (!outfile)
{
fprintf(stderr, "Could not run zwrite\n");
if (inbuff)
free(inbuff);
return FALSE;
}
}
out = ciphers[cipher].encrypt(keyfile, inbuff, buflen, outfile);
if (zephyr)
CloseZephyrPipe(outfile);
free(inbuff);
return out;
}
int do_encrypt_des(const char *keyfile, const char *in, int length, FILE *outfile)
{
des_key_schedule schedule;
unsigned char input[8], output[8];
const char *inptr;
int num_blocks, last_block_size;
char *keystring;
int size;
keystring = read_keystring(keyfile);
if(!keystring) {
return FALSE;
}
owl_zcrypt_string_to_schedule(keystring, &schedule);
free(keystring);
inptr = in;
num_blocks = (length + 7) / 8;
last_block_size = ((length + 7) % 8) + 1;
/* Encrypt the input (inbuff or stdin) and send it to outfile */
while (TRUE)
{
/* Get 8 bytes from buffer */
if (num_blocks > 1)
{
size = 8;
memcpy(input, inptr, size);
inptr += 8;
num_blocks--;
}
else if (num_blocks == 1)
{
size = last_block_size;
memcpy(input, inptr, size);
num_blocks--;
}
else
size = 0;
/* Check for EOF and pad the string to 8 chars, if needed */
if (size == 0)
break;
if (size < 8)
memset(input + size, 0, 8 - size);
/* Encrypt and output the block */
des_ecb_encrypt(&input, &output, schedule, TRUE);
block_to_ascii(output, outfile);
if (size < 8)
break;
}
putc('\n', outfile);
return TRUE;
}
int do_encrypt_aes(const char *keyfile, const char *in, int length, FILE *outfile)
{
char *out;
int err, status;
const char *argv[] = {
"gpg",
"--symmetric",
"--no-options",
"--no-default-keyring",
"--keyring", "/dev/null",
"--secret-keyring", "/dev/null",
"--batch",
"--quiet",
"--no-use-agent",
"--armor",
"--cipher-algo", "AES",
"--passphrase-file", keyfile,
NULL
};
err = call_filter(argv, in, &out, &status);
if(err || status) {
g_free(out);
return FALSE;
}
fwrite(out, strlen(out), 1, outfile);
g_free(out);
return TRUE;
}
/* Read a half-byte from stdin, skipping invalid characters. Returns -1
if at EOF or file error */
int read_ascii_nybble(void)
{
char c;
while (TRUE)
{
if (fread(&c, 1, 1, stdin) == 0)
return -1;
else if (c >= BASE_CODE && c <= LAST_CODE)
return c - BASE_CODE;
}
}
/* Read both halves of the byte and return the single byte. Returns -1
if at EOF or file error. */
int read_ascii_byte(void)
{
int c1, c2;
c1 = read_ascii_nybble();
if (c1 >= 0)
{
c2 = read_ascii_nybble();
if (c2 >= 0)
{
return c1 * 0x10 + c2;
}
}
return -1;
}
/* Read an 8-byte DES block from stdin */
int read_ascii_block(unsigned char *input)
{
int c;
int i;
for (i = 0; i < 8; i++)
{
c = read_ascii_byte();
if (c < 0)
return FALSE;
input[i] = c;
}
return TRUE;
}
/* Decrypt stdin */
int do_decrypt(const char *keyfile, int cipher)
{
return ciphers[cipher].decrypt(keyfile);
}
int do_decrypt_aes(const char *keyfile) {
char *in, *out;
int length;
const char *argv[] = {
"gpg",
"--decrypt",
"--no-options",
"--no-default-keyring",
"--keyring", "/dev/null",
"--secret-keyring", "/dev/null",
"--batch",
"--no-use-agent",
"--quiet",
"--passphrase-file", keyfile,
NULL
};
int err, status;
in = slurp_stdin(TRUE, &length);
if(!in) return FALSE;
err = call_filter(argv, in, &out, &status);
free(in);
if(err || status) {
g_free(out);
return FALSE;
}
fwrite(out, strlen(out), 1, stdout);
g_free(out);
return TRUE;
}
int do_decrypt_des(const char *keyfile) {
des_key_schedule schedule;
unsigned char input[8], output[8];
char tmp[9];
char *keystring;
/*
DES decrypts 8 bytes at a time. We copy those over into the 9-byte
'tmp', which has the final byte zeroed, to ensure that we always
have a NULL-terminated string we can call printf/strlen on.
We don't pass 'tmp' to des_ecb_encrypt directly, because it's
prototyped as taking 'unsigned char[8]', and this avoids a stupid
cast.
We zero 'tmp' entirely, not just the final byte, in case there are
no input blocks.
*/
memset(tmp, 0, sizeof tmp);
keystring = read_keystring(keyfile);
if(!keystring) return FALSE;
owl_zcrypt_string_to_schedule(keystring, &schedule);
free(keystring);
while (read_ascii_block(input))
{
des_ecb_encrypt(&input, &output, schedule, FALSE);
memcpy(tmp, output, 8);
printf("%s", tmp);
}
if (!tmp[0] || tmp[strlen(tmp) - 1] != '\n')
printf("\n");
return TRUE;
}
Jump to Line
Something went wrong with that request. Please try again.