source: zcrypt.c @ 6b1c3b6

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