source: zcrypt.c @ edae037

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