source: zcrypt.c @ a08bfc3

release-1.10release-1.6release-1.7release-1.8release-1.9
Last change on this file since a08bfc3 was a08bfc3, checked in by Nelson Elhage <nelhage@ksplice.com>, 14 years ago
zcrypt: Send errors to stderr, not stdout.
  • Property mode set to 100644
File size: 16.2 KB
RevLine 
[edae037]1/* zcrypt.c -- Read in a data stream from stdin & dump a decrypted/encrypted *
2 *   datastream.  Reads the string to make the key from from the first       *
3 *   parameter.  Encrypts or decrypts according to -d or -e flag.  (-e is    *
4 *   default.)  Will invoke zwrite if the -c option is provided for          *
5 *   encryption.  If a zephyr class is specified & the keyfile name omitted  *
6 *   the ~/.crypt-table will be checked for "crypt-classname" and then       *
7 *   "crypt-default" for the keyfile name.                                   */
8
9#include <stdio.h>
[6b1c3b6]10
[edae037]11#include <unistd.h>
12#include <sys/types.h>
13#include <zephyr/zephyr.h>
[6b1c3b6]14#include <glib.h>
[356465e]15#include <string.h>
16#include <stdlib.h>
17#include <sys/wait.h>
[edae037]18
[49bc81e]19#ifdef HAVE_KERBEROS_IV
20#include <kerberosIV/des.h>
21#else
22#include <openssl/des.h>
23#endif
24
[edae037]25#define MAX_KEY 128
26
27#ifndef TRUE
28#define TRUE -1
29#endif
30#ifndef FALSE
31#define FALSE 0
32#endif
33
34#define ZWRITE_OPT_NOAUTH     (1<<0)
35#define ZWRITE_OPT_SIGNATURE  (1<<1)
36#define ZWRITE_OPT_IGNOREVARS (1<<2)
37#define ZWRITE_OPT_VERBOSE    (1<<3)
38#define ZWRITE_OPT_QUIET      (1<<4)
39#define ZCRYPT_OPT_MESSAGE    (1<<5)
40#define ZCRYPT_OPT_IGNOREDOT  (1<<6)
41
42typedef struct
43{
44  int flags;
45  char *signature;
46  char *message;
47} ZWRITEOPTIONS;
48
49char *GetZephyrVarKeyFile(char *whoami, char *class, char *instance);
50char *BuildArgString(char **argv, int start, int end);
51int do_encrypt(char *keystring, int zephyr, char *class, char *instance,
52          ZWRITEOPTIONS *zoptions, char* keyfile);
[356465e]53void do_decrypt(char *keystring);
[edae037]54
55#define M_NONE            0
56#define M_ZEPHYR_ENCRYPT  1
57#define M_DECRYPT         2
58#define M_ENCRYPT         3
59#define M_RANDOMIZE       4
60#define M_SETKEY          5
61
[356465e]62static void owl_zcrypt_string_to_schedule(char *keystring, des_key_schedule schedule) {
63#ifdef HAVE_KERBEROS_IV
64  des_cblock key;
65#else
66  des_cblock _key, *key = &_key;
67#endif
68
69  des_string_to_key(keystring, key);
70  des_key_sched(key, schedule);
71}
72
73int main(int argc, char *argv[])
[edae037]74{
75  char *fname = NULL;
76  int error = FALSE;
77  int zephyr = FALSE;
78  char *class = NULL, *instance = NULL;
79  int mode = M_NONE;
80
81  extern int optind, opterr;
82  extern char *optarg;
83  char c;
84
85  int messageflag = FALSE;
86  ZWRITEOPTIONS zoptions;
87  zoptions.flags = 0;
88
89  while ((c = getopt(argc, argv, "ZDERSF:c:i:advqtluons:f:m")) != (char)EOF)
90  {
91    switch(c)
92    {
93      case 'Z':
94        /* Zephyr encrypt */
95        mode = M_ZEPHYR_ENCRYPT;
96        break;
97      case 'D':
98        /* Decrypt */
99        mode = M_DECRYPT;
100        break;
101      case 'E':
102        /* Encrypt */
103        mode = M_ENCRYPT;
104        break;
105      case 'R':
106        /* Randomize the keyfile */
107        mode = M_RANDOMIZE;
108        break;
109      case 'S':
110        /* Set a new key value from stdin */
111        mode = M_SETKEY;
112        break;
113      case 'F':
114        /* Specify the keyfile explicitly */
115        if (fname != NULL) error = TRUE;
116        fname = optarg;
117        break;
118      case 'c':
119        /* Zwrite/zcrypt: class name */
120        if (class != NULL) error = TRUE;
121        class = optarg;
122        break;
123      case 'i':
124        /* Zwrite/zcrypt: instance name */
125        if (instance != NULL) error = TRUE;
126        instance = optarg;
127        break;
128      case 'a':
129        /* Zwrite: authenticate (default) */
130        zoptions.flags &= ~ZWRITE_OPT_NOAUTH;
131        break;
132      case 'd':
133        /* Zwrite: do not authenticate */
134        zoptions.flags |= ZWRITE_OPT_NOAUTH;
135        break;
136      case 'v':
137        /* Zwrite: verbose */
138        zoptions.flags |= ZWRITE_OPT_VERBOSE;
139        break;
140      case 'q':
141        /* Zwrite: quiet */
142        zoptions.flags |= ZWRITE_OPT_QUIET;
143        break;
144      case 't':
145        /* Zwrite: no expand tabs (ignored) */
146        break;
147      case 'l':
148        /* Zwrite: ignore '.' on a line by itself (ignored) */
149        zoptions.flags |= ZCRYPT_OPT_IGNOREDOT;
150        break;
151      case 'u':
152        /* Zwrite: urgent message */
153        instance = "URGENT";
154        break;
155      case 'o':
156        /* Zwrite: ignore zephyr variables zwrite-class, zwrite-inst, */
157        /*         zwrite-opcode */
158        zoptions.flags |= ZWRITE_OPT_IGNOREVARS;
159        break;
160      case 'n':
161        /* Zwrite: prevent PING message (always used) */
162        break;
163      case 's':
164        /* Zwrite: signature */
165        zoptions.flags |= ZWRITE_OPT_SIGNATURE;
166        zoptions.signature = optarg;
167        break;
168      case 'f':
169        /* Zwrite: file system specification (ignored) */
170        break;
171      case 'm':
172        /* Message on rest of line*/
173        messageflag = TRUE;
174        break;
175      case '?':
176        error = TRUE;
177        break;
178    }
179    if (error || messageflag)
180      break;
181  }
182
183  if (class != NULL || instance != NULL)
184    zephyr = TRUE;
185
186  if (messageflag)
187  {
188    zoptions.flags |= ZCRYPT_OPT_MESSAGE;
189    zoptions.message = BuildArgString(argv, optind, argc);
190    if (!zoptions.message)
191    {
[a08bfc3]192      fprintf(stderr, "Memory allocation error.\n");
[edae037]193      error = TRUE;
194    }
195  }
196  else if (optind < argc)
197  {
198    error = TRUE;
199  }
200
201  if (mode == M_NONE)
202    mode = (zephyr?M_ZEPHYR_ENCRYPT:M_ENCRYPT);
203
204  if (mode == M_ZEPHYR_ENCRYPT && !zephyr)
205    error = TRUE;
206
207  if (!error && fname == NULL && (class != NULL || instance != NULL))
208    fname = GetZephyrVarKeyFile(argv[0], class, instance);
209 
210  if (error || fname == NULL)
211  {
[a08bfc3]212    fprintf(stderr, "Usage: %s [-Z|-D|-E|-R|-S] [-F Keyfile] [-c class] [-i instance]\n", argv[0]);
213    fprintf(stderr, "       [-advqtluon] [-s signature] [-f arg] [-m message]\n");
214    fprintf(stderr, "  One or more of class, instance, and keyfile must be specified.\n");
[edae037]215  }
216  else
217  {
218    if (mode == M_RANDOMIZE)
219    {
220      /* Choose a new, random key */
221/*
222      FILE *fkey = fopen(fname, "w");
223      if (!fkey)
224        printf("Could not open key file for writing: %s\n", fname);
225      else
226      {
227        char string[100];
228        fputs(fkey, string);
229        fclose(fkey);
230        }
231 */
[a08bfc3]232      fprintf(stderr, "Feature not yet implemented.\n");
[edae037]233    }
234    else if (mode == M_SETKEY)
235    {
236      /* Set a new, user-entered key */
237      char newkey[MAX_KEY];
238      FILE *fkey;
239
240      if (isatty(0))
241      {
242        printf("Enter new key: ");
243        /* Really should read without echo!!! */
244      }
[356465e]245      if(!fgets(newkey, MAX_KEY - 1, stdin)) {
246        fprintf(stderr, "Error reading key.\n");
247        return 1;
248      }
[edae037]249
250      fkey = fopen(fname, "w");
251      if (!fkey)
[a08bfc3]252        fprintf(stderr, "Could not open key file for writing: %s\n", fname);
[edae037]253      else
254      {
255        if (fputs(newkey, fkey) != strlen(newkey) || putc('\n', fkey) != '\n')
256        {
[a08bfc3]257          fprintf(stderr, "Error writing to key file.\n");
[edae037]258          fclose(fkey);
259        }
260        else
261        {
262          fclose(fkey);
[a08bfc3]263          fprintf(stderr, "Key update complete.\n");
[edae037]264        }
265      }
266    }
267    else
268    {
269      /* Encrypt/decrypt */
270      FILE *fkey = fopen(fname, "r");
271      if (!fkey)
[a08bfc3]272        fprintf(stderr, "Could not open key file: %s\n", fname);
[edae037]273      else
274      {
275        char keystring[MAX_KEY];
[356465e]276        if(!fgets(keystring, MAX_KEY-1, fkey)) {
277          fclose(fkey);
278          fprintf(stderr, "Error reading key file.\n");
279          return 1;
280        }
[edae037]281        if (mode == M_ZEPHYR_ENCRYPT || mode == M_ENCRYPT)
282          do_encrypt(keystring, (mode == M_ZEPHYR_ENCRYPT), class, instance,
283                     &zoptions, fname);
284        else
285          do_decrypt(keystring);
286        fclose(fkey);
287      }
288    }
289  }
290
291  /* Always print the **END** message if -D is specified. */
292  if (mode == M_DECRYPT)
293    printf("**END**\n");
[356465e]294  return 0;
[edae037]295}
296
297/* Build a space-separated string from argv from elements between start  *
298 * and end - 1.  malloc()'s the returned string. */
299char *BuildArgString(char **argv, int start, int end)
300{
301  int len = 1;
302  int i;
303  char *result;
304
305  /* Compute the length of the string.  (Plus 1 or 2) */
306  for (i = start; i < end; i++)
307    len += strlen(argv[i]) + 1;
308
309  /* Allocate memory */
310  result = (char *)malloc(len);
311  if (result)
312  {
313    /* Build the string */
314    char *ptr = result;
315    /* Start with an empty string, in case nothing is copied. */
316    *ptr = '\0';
317    /* Copy the arguments */
318    for (i = start; i < end; i++)
319    {
320      char *temp = argv[i];
321      /* Add a space, if not the first argument */
322      if (i != start)
323        *ptr++ = ' ';
324      /* Copy argv[i], leaving ptr pointing to the '\0' copied from temp */
[356465e]325      while ((*ptr = *temp++))
[edae037]326        ptr++;
327    }
328  }
329
330  return result;
331}
332
333#define MAX_BUFF 258
334#define MAX_SEARCH 3
335/* Find the class/instance in the .crypt-table */
336char *GetZephyrVarKeyFile(char *whoami, char *class, char *instance)
337{
[356465e]338  char *keyfile = NULL;
[6b1c3b6]339  char *varname[MAX_SEARCH];
[edae037]340  int length[MAX_SEARCH], i;
341  char buffer[MAX_BUFF];
[6b1c3b6]342  char *filename;
[edae037]343  char result[MAX_SEARCH][MAX_BUFF];
344  int numsearch = 0;
345  FILE *fsearch;
346
[6b1c3b6]347  memset(varname, 0, sizeof(varname));
348
[edae037]349  /* Determine names to look for in .crypt-table */
350  if (instance)
[6b1c3b6]351    varname[numsearch++] = g_strdup_printf("crypt-%s-%s:", (class?class:"message"), instance);
[edae037]352  if (class)
[6b1c3b6]353    varname[numsearch++] = g_strdup_printf("crypt-%s:", class);
354  varname[numsearch++] = g_strdup("crypt-default:");
[edae037]355
356  /* Setup the result array, and determine string lengths */
357  for (i = 0; i < numsearch; i++)
358  {
359    result[i][0] = '\0';
360    length[i] = strlen(varname[i]);
361  }
362
363  /* Open~/.crypt-table */
[6b1c3b6]364  filename = g_strdup_printf("%s/.crypt-table", getenv("HOME"));
[edae037]365  fsearch = fopen(filename, "r");
366  if (fsearch)
367  {
368    /* Scan file for a match */
369    while (!feof(fsearch))
370    {
[356465e]371      if (!fgets(buffer, MAX_BUFF - 3, fsearch)) break;
[edae037]372      for (i = 0; i < numsearch; i++)
373        if (strncasecmp(varname[i], buffer, length[i]) == 0)
374        {
375          int j;
376          for (j = length[i]; buffer[j] == ' '; j++)
377            ;
378          strcpy(result[i], &buffer[j]);
379          if (*result[i])
380            if (result[i][strlen(result[i])-1] == '\n')
381              result[i][strlen(result[i])-1] = '\0';
382        }
383    }
384
385    /* Pick the "best" match found */
386    keyfile = NULL;
387    for (i = 0; i < numsearch; i++)
388      if (*result[i])
389      {
390        keyfile = result[i];
391        break;
392      }
393
394    if (keyfile == NULL)
395    {
[a08bfc3]396      fprintf(stderr, "Could not find key table entry.\n");
[edae037]397    }
398    else
399    {
400      /* Prepare result to be returned */
401      char *temp = keyfile;
402      keyfile = (char *)malloc(strlen(temp) + 1);
403      if (keyfile)
404        strcpy(keyfile, temp);
405      else
[a08bfc3]406        fprintf(stderr, "Memory allocation error.\n");
[edae037]407    }
408   
409    fclose(fsearch);
410  }
411  else
[a08bfc3]412    fprintf(stderr, "Could not open key table file: %s\n", filename);
[edae037]413
[6b1c3b6]414  for(i = 0; i < MAX_SEARCH; i++) {
415    if(varname[i] != NULL) {
416      g_free(varname[i]);
417    }
418  }
419
420  if(filename != NULL) {
421    g_free(filename);
422  }
423
[edae037]424  return keyfile;
425}
426
427static pid_t zephyrpipe_pid = 0;
428
429/* Open a pipe to zwrite */
430FILE *GetZephyrPipe(char *class, char *instance, ZWRITEOPTIONS *zoptions)
431{
432  int fildes[2];
433  pid_t pid;
434  FILE *result;
[356465e]435  char *argv[20];
436  int argc = 0;
[edae037]437
438  if (pipe(fildes) < 0)
439    return NULL;
440  pid = fork();
441
442  if (pid < 0)
443  {
444    /* Error: clean up */
445    close(fildes[0]);
446    close(fildes[1]);
447    result = NULL;
448  }
449  else if (pid == 0)
450  {
451    /* Setup child process */
452    argv[argc++] = "zwrite";
453    argv[argc++] = "-n";     /* Always send without ping */
454    if (class)
455    {
456      argv[argc++] = "-c";
457      argv[argc++] = class;
458    }
459    if (instance)
460    {
461      argv[argc++] = "-i";
462      argv[argc++] = instance;
463    }
464    if (zoptions->flags & ZWRITE_OPT_NOAUTH)
465      argv[argc++] = "-d";
466    if (zoptions->flags & ZWRITE_OPT_QUIET)
467      argv[argc++] = "-q";
468    if (zoptions->flags & ZWRITE_OPT_VERBOSE)
469      argv[argc++] = "-v";
470    if (zoptions->flags & ZWRITE_OPT_SIGNATURE)
471    {
472      argv[argc++] = "-s";
473      argv[argc++] = zoptions->signature;
474    }
475    argv[argc++] = "-O";
476    argv[argc++] = "crypt";
477    argv[argc] = NULL;
478    close(fildes[1]);
479    if (fildes[0] != STDIN_FILENO)
480    {
481      if (dup2(fildes[0], STDIN_FILENO) != STDIN_FILENO)
482        exit(0);
483      close(fildes[0]);
484    }
485    close(fildes[0]);
486    execvp(argv[0], argv);
[a08bfc3]487    fprintf(stderr, "Exec error: could not run zwrite\n");
[edae037]488    exit(0);
489  }
490  else
491  {
492    close(fildes[0]);
493    /* Create a FILE * for the zwrite pipe */
494    result = (FILE *)fdopen(fildes[1], "w");
495    zephyrpipe_pid = pid;
496  }
497
498  return result;
499}
500
501/* Close the pipe to zwrite */
[356465e]502void CloseZephyrPipe(FILE *pipe)
[edae037]503{
504  fclose(pipe);
505  waitpid(zephyrpipe_pid, NULL, 0);
506  zephyrpipe_pid = 0;
507}
508
509#define MAX_RESULT 2048
510
511#define BASE_CODE 70
512#define LAST_CODE (BASE_CODE + 15)
513#define OUTPUT_BLOCK_SIZE 16
514
[356465e]515void block_to_ascii(unsigned char *output, FILE *outfile)
[edae037]516{
517  int i;
518  for (i = 0; i < 8; i++)
519  {
520    putc(((output[i] & 0xf0) >> 4) + BASE_CODE, outfile);
521    putc( (output[i] & 0x0f)       + BASE_CODE, outfile);
522  }
523}
524
525#define MAX_LINE 128
526
527/* Encrypt stdin, with prompt if isatty, and send to stdout, or to zwrite
528   if zephyr is set. */
529int do_encrypt(char *keystring, int zephyr, char *class, char *instance,
530          ZWRITEOPTIONS *zoptions, char* keyfile)
531{
532  des_key_schedule schedule;
[356465e]533  unsigned char input[8], output[8];
[edae037]534  int size;
535  FILE *outfile = stdout;
536  int error = FALSE;
537  char *inbuff = NULL, *inptr;
538  int freein = FALSE;
539  int use_buffer = FALSE;
[356465e]540  int num_blocks = 0, last_block_size = 0;
[edae037]541
[356465e]542  owl_zcrypt_string_to_schedule(keystring, schedule);
[edae037]543
544  if (zephyr)
545  {
546    if (zoptions->flags & ZCRYPT_OPT_MESSAGE)
547    {
548      /* Use the -m message */
549      int length;
550      inbuff = zoptions->message;     
551      length = strlen(inbuff);
552      num_blocks = (length + 7) / 8;
553      last_block_size = ((length + 7) % 8) + 1;
554      use_buffer = TRUE;
555    }
556    else if (isatty(0))
557    {
558      /* tty input, so show the "Type your message now..." message */
559      if (zoptions->flags & ZCRYPT_OPT_IGNOREDOT)
560        printf("Type your message now.  End with the end-of-file character.\n");
561      else
562        printf("Type your message now.  End with control-D or a dot on a line by itself.\n");
563      use_buffer = TRUE;
564      if ((inptr = inbuff = (char *)malloc(MAX_RESULT)) == NULL)
565      {
[a08bfc3]566        fprintf(stderr, "Memory allocation error\n");
[edae037]567        return FALSE;
568      }
569      while (inptr - inbuff < MAX_RESULT - MAX_LINE - 20)
570      {
[356465e]571        if (!fgets(inptr, MAX_LINE, stdin))
572          return FALSE;
[edae037]573        if (inptr[0])
574        {
575          if (inptr[0] == '.' && inptr[1] == '\n' && 
576              !(zoptions->flags & ZCRYPT_OPT_IGNOREDOT))
577          {
578            inptr[0] = '\0';
579            break;
580          }
581          else
582            inptr += strlen(inptr);
583        }
584        else
585          break;
586      }
587      num_blocks = (inptr - inbuff + 7) / 8;
588      last_block_size = ((inptr - inbuff + 7) % 8) + 1;
589      freein = TRUE;
590    }
591
592    /* if (zephyr) */
593    outfile = GetZephyrPipe(class, instance, zoptions);
594    if (!outfile)
595    {
[a08bfc3]596      fprintf(stderr, "Could not run zwrite\n");
[edae037]597      if (freein && inbuff)
598        free(inbuff);
599      return FALSE;
600    }
601  }
602
603  inptr = inbuff;
604
605  /* Encrypt the input (inbuff or stdin) and send it to outfile */
606  while (TRUE)
607  {
608    if (use_buffer)
609    {
610      /* Get 8 bytes from buffer */
611      if (num_blocks > 1)
612      {
613        size = 8;
614        memcpy(input, inptr, size);
615        inptr += 8;
616        num_blocks--;
617      }
618      else if (num_blocks == 1)
619      {
620        size = last_block_size;
621        memcpy(input, inptr, size);
622        num_blocks--;
623      }
624      else
625        size = 0;
626    }
627    else
628      /* Get 8 bytes from stdin */
629      size = fread(input, 1, 8, stdin);
630
631    /* Check for EOF and pad the string to 8 chars, if needed */
632    if (size == 0)
633      break;                      /* END OF INPUT: BREAK FROM while LOOP! */
634    if (size < 8)
635      memset(input + size, 0, 8 - size);
636
637    /* Encrypt and output the block */
[356465e]638    des_ecb_encrypt(&input, &output, schedule, TRUE);
[edae037]639    block_to_ascii(output, outfile);
640
641    if (size < 8)
642      break;
643  }
644
645  /* Close out the output */
646  if (!error)
647    putc('\n', outfile);
648  if (zephyr)
649    CloseZephyrPipe(outfile);
650
651  /* Free the input buffer, if necessary */
652  if (freein && inbuff)
653    free(inbuff);
654
655  return !error;
656}
657
658/* Read a half-byte from stdin, skipping invalid characters.  Returns -1
659   if at EOF or file error */
[356465e]660int read_ascii_nybble(void)
[edae037]661{
662  char c;
663
664  while (TRUE)
665  {
666    if (fread(&c, 1, 1, stdin) == 0)
667      return -1;
668    else if (c >= BASE_CODE && c <= LAST_CODE)
669      return c - BASE_CODE;
670  }
671}
672
673/* Read both halves of the byte and return the single byte.  Returns -1
674   if at EOF or file error. */
[356465e]675int read_ascii_byte(void)
[edae037]676{
677  int c1, c2;
678  c1 = read_ascii_nybble();
679  if (c1 >= 0)
680  {
681    c2 = read_ascii_nybble();
682    if (c2 >= 0)
683    {
684      return c1 * 0x10 + c2;
685    }
686  }
687  return -1;
688}
689
690/* Read an 8-byte DES block from stdin */
[356465e]691int read_ascii_block(unsigned char *input)
[edae037]692{
693  int c;
694
695  int i;
696  for (i = 0; i < 8; i++)
697  {
698    c = read_ascii_byte();
699    if (c < 0)
700      return FALSE;
701
702    input[i] = c;
703  }
704
705  return TRUE;
706}
707
708/* Decrypt stdin */
[356465e]709void do_decrypt(char *keystring)
[edae037]710{
711  des_key_schedule schedule;
[356465e]712  unsigned char input[8], output[8];
[edae037]713
714  output[0] = '\0';    /* In case no message at all                 */
715
[356465e]716  owl_zcrypt_string_to_schedule(keystring, schedule);
[edae037]717
718  while (read_ascii_block(input))
719  {
[356465e]720    des_ecb_encrypt(&input, &output, schedule, FALSE);
[edae037]721    printf("%s", output);
722  }
723
[356465e]724  if (!output[0] || output[strlen((const char*)output) - 1] != '\n')
[edae037]725      printf("\n");
726}
Note: See TracBrowser for help on using the repository browser.