/* Copyright (c) 2002,2003,2004,2009 James M. Kretchmar
*
* This file is part of Owl.
*
* Owl is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Owl 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 Owl. If not, see .
*
* ---------------------------------------------------------------
*
* As of Owl version 2.1.12 there are patches contributed by
* developers of the branched BarnOwl project, Copyright (c)
* 2006-2009 The BarnOwl Developers. All rights reserved.
*/
/* The functions here with "owl" in their name were written for the
* Owl project. Other code is from Philip Lisiecki's
* (lisiecki@mit.edu) zcrypt program.
*/
/* 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. */
static const char fileIdent[] = "$Id$";
#include
#include
#include
#include
#include
#include "owl.h"
#ifdef OWL_ENABLE_ZCRYPT
#define BASE_CODE 70
#define LAST_CODE (BASE_CODE + 15)
#define OUTPUT_BLOCK_SIZE 16
#include
#include
#include
#define MAX_KEY 128
#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;
char *signature;
char *message;
} ZWRITEOPTIONS;
char *GetZephyrVarKeyFile(char *whoami, char *class, char *instance);
char *BuildArgString(char **argv, int start, int end);
static int do_encrypt(char *keystring, int zephyr, char *class, char *instance, ZWRITEOPTIONS *zoptions, char* keyfile);
int do_decrypt(char *keystring);
#ifndef HAVE_DES_ECB_ENCRYPT_PROTO
int des_ecb_encrypt(char [], char [], des_key_schedule, int);
#endif
#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
int zcrypt(int argc, char *argv[]) {
char *fname = NULL;
int error = FALSE;
int zephyr = FALSE;
char *class = NULL, *instance = NULL;
int mode = M_NONE;
extern int optind, opterr;
extern char *optarg;
char c;
int messageflag = FALSE;
ZWRITEOPTIONS zoptions;
zoptions.flags = 0;
while ((c = getopt(argc, argv, "ZDERSF:c:i:advqtluons:f:m")) != (char)EOF) {
switch(c) {
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 (fname != NULL) error = TRUE;
fname = 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)
{
printf("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 && fname == NULL && (class != NULL || instance != NULL)) {
fname = GetZephyrVarKeyFile(argv[0], class, instance);
}
if (error || fname == NULL) {
printf("Usage: %s [-Z|-D|-E|-R|-S] [-F Keyfile] [-c class] [-i instance]\n", argv[0]);
printf(" [-advqtluon] [-s signature] [-f arg] [-m message]\n");
printf(" One or more of class, instance, and keyfile must be specified.\n");
} else {
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);
}
*/
printf("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!!! */
fgets(newkey, MAX_KEY - 1, stdin);
} else {
fgets(newkey, MAX_KEY - 1, stdin);
}
fkey = fopen(fname, "w");
if (!fkey) {
printf("Could not open key file for writing: %s\n", fname);
} else {
if (fputs(newkey, fkey) != strlen(newkey) || putc('\n', fkey) != '\n') {
printf("Error writing to key file.\n");
fclose(fkey);
} else {
fclose(fkey);
printf("Key update complete.\n");
}
}
} else {
/* Encrypt/decrypt */
FILE *fkey = fopen(fname, "r");
if (!fkey) {
printf("Could not open key file: %s\n", fname);
} else {
char keystring[MAX_KEY];
fgets(keystring, MAX_KEY-1, fkey);
if (mode == M_ZEPHYR_ENCRYPT || mode == M_ENCRYPT) {
do_encrypt(keystring, (mode == M_ZEPHYR_ENCRYPT), class, instance,
&zoptions, fname);
} else {
do_decrypt(keystring);
}
fclose(fkey);
}
}
}
/* Always print the **END** message if -D is specified. */
if (mode == M_DECRYPT) printf("**END**\n");
exit(0);
}
/* The 'owl_zcrypt_decrypt' function was written by kretch for Owl.
* Decrypt the message in 'in' on class 'class' and instance
* 'instance' and leave the result in 'out'. Out must be a buffer
* allocated by the caller.
*
* return 0 on success, otherwise -1
*/
int owl_zcrypt_decrypt(char *out, char *in, char *class, char *instance) {
char *fname, keystring[MAX_KEY], *inptr, *endptr;
FILE *fkey;
des_cblock key;
des_key_schedule schedule;
char input[8], output[9];
int i, c1, c2;
fname=GetZephyrVarKeyFile("zcrypt", class, instance);
if (!fname) return(-1);
fkey=fopen(fname, "r");
if (!fkey) return(-1);
fgets(keystring, MAX_KEY-1, fkey);
fclose(fkey);
strcpy(out, "");
output[0] = '\0'; /* In case no message at all */
output[8] = '\0'; /* NULL at end will limit string length to 8 */
des_string_to_key(keystring, key);
des_key_sched(key, schedule);
inptr=in;
endptr=in+strlen(in)-1;
while (inptr 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; /* END OF INPUT: BREAK FROM while LOOP! */
if (size<8) memset(input + size, 0, 8 - size);
/* Encrypt and output the block */
des_ecb_encrypt(input, output, schedule, TRUE);
for (i = 0; i < 8; i++) {
sprintf(out + strlen(out), "%c", ((output[i] & 0xf0) >> 4) + BASE_CODE);
sprintf(out + strlen(out), "%c", (output[i] & 0x0f) + BASE_CODE);
}
if (size < 8) break;
}
return(0);
}
/* Build a space-separated string from argv from elements between start *
* and end - 1. malloc()'s the returned string. */
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++)!=0) {
ptr++;
}
}
}
return(result);
}
#define MAX_BUFF 258
#define MAX_SEARCH 3
/* Find the class/instance in the .crypt-table */
char *GetZephyrVarKeyFile(char *whoami, char *class, char *instance) {
char *keyfile = NULL;
char *varname[MAX_SEARCH];
int length[MAX_SEARCH], i;
char buffer[MAX_BUFF];
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++] = owl_sprintf("crypt-%s-%s:", (class?class:"message"), instance);
}
if (class) {
varname[numsearch++] = owl_sprintf("crypt-%s:", class);
}
varname[numsearch++] = owl_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 */
filename = owl_sprintf("%s/.crypt-table", getenv("HOME"));
fsearch = fopen(filename, "r");
if (fsearch) {
/* Scan file for a match */
while (!feof(fsearch)) {
fgets(buffer, MAX_BUFF - 3, fsearch);
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) {
/* printf("Could not find key table entry.\n"); */
} else {
/* Prepare result to be returned */
char *temp = keyfile;
keyfile = (char *)malloc(strlen(temp) + 1);
if (keyfile) {
strcpy(keyfile, temp);
} else {
/* printf("Memory allocation error.\n"); */
}
}
fclose(fsearch);
} else {
/* printf("Could not open key table file: %s\n", filename); */
}
for(i = 0; i < MAX_SEARCH; i++) {
owl_free(varname[i]);
}
owl_free(filename);
return(keyfile);
}
static pid_t zephyrpipe_pid = 0;
/* Open a pipe to zwrite */
static FILE *GetZephyrPipe(char *class, char *instance, ZWRITEOPTIONS *zoptions) {
int fildes[2];
pid_t pid;
FILE *result;
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], argv);
printf("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 MAX_RESULT 2048
void block_to_ascii(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);
}
}
#define MAX_LINE 128
/* Encrypt stdin, with prompt if isatty, and send to stdout, or to zwrite
if zephyr is set. */
static int do_encrypt(char *keystring, int zephyr, char *class, char *instance, ZWRITEOPTIONS *zoptions, char* keyfile) {
des_cblock key;
des_key_schedule schedule;
char input[8], output[8];
int size;
FILE *outfile = stdout;
int error = FALSE;
char *inbuff = NULL, *inptr;
int freein = FALSE;
int use_buffer = FALSE;
int num_blocks=0, last_block_size=0;
des_string_to_key(keystring, key);
des_key_sched(key, schedule);
if (zephyr) {
if (zoptions->flags & ZCRYPT_OPT_MESSAGE) {
/* Use the -m message */
int length;
inbuff = zoptions->message;
length = strlen(inbuff);
num_blocks = (length + 7) / 8;
last_block_size = ((length + 7) % 8) + 1;
use_buffer = TRUE;
} 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");
}
use_buffer = TRUE;
if ((inptr = inbuff = (char *)malloc(MAX_RESULT)) == NULL) {
printf("Memory allocation error\n");
return FALSE;
}
while (inptr - inbuff < MAX_RESULT - MAX_LINE - 20) {
fgets(inptr, MAX_LINE, stdin);
if (inptr[0]) {
if (inptr[0] == '.' && inptr[1] == '\n' &&
!(zoptions->flags & ZCRYPT_OPT_IGNOREDOT)) {
inptr[0] = '\0';
break;
} else {
inptr += strlen(inptr);
}
} else {
break;
}
}
num_blocks = (inptr - inbuff + 7) / 8;
last_block_size = ((inptr - inbuff + 7) % 8) + 1;
freein = TRUE;
}
/* if (zephyr) */
outfile = GetZephyrPipe(class, instance, zoptions);
if (!outfile) {
printf("Could not run zwrite\n");
if (freein && inbuff) {
free(inbuff);
}
return(FALSE);
}
}
inptr = inbuff;
/* Encrypt the input (inbuff or stdin) and send it to outfile */
while (TRUE) {
if (use_buffer) {
/* 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;
}
} else {
/* Get 8 bytes from stdin */
size = fread(input, 1, 8, stdin);
}
/* Check for EOF and pad the string to 8 chars, if needed */
if (size == 0) break; /* END OF INPUT: BREAK FROM while LOOP! */
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;
}
/* Close out the output */
if (!error) putc('\n', outfile);
if (zephyr) CloseZephyrPipe(outfile);
/* Free the input buffer, if necessary */
if (freein && inbuff) free(inbuff);
return(!error);
}
/* Read a half-byte from stdin, skipping invalid characters. Returns -1
if at EOF or file error */
int read_ascii_nybble() {
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() {
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(char *input) {
int c, 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(char *keystring) {
des_cblock key;
des_key_schedule schedule;
char input[8], output[9];
output[0] = '\0'; /* In case no message at all */
output[8] = '\0'; /* NULL at end will limit string length to 8 */
des_string_to_key(keystring, key);
des_key_sched(key, schedule);
while (read_ascii_block(input)) {
des_ecb_encrypt(input, output, schedule, FALSE);
printf("%s", output);
}
if (output[0]) {
if (output[strlen(output)-1] != '\n') {
printf("\n");
}
} else {
printf("\n");
}
return(0);
}
#endif