source: zcrypt.c @ 4133e34

release-1.6release-1.7release-1.8release-1.9
Last change on this file since 4133e34 was 4133e34, checked in by Nelson Elhage <nelhage@ksplice.com>, 11 years ago
zcrypt: Improve error handling behavior slightly. * Don't print usage if we have valid usage, but couldn't find a key. * Exit with an appropriate exit code
  • Property mode set to 100644
File size: 16.3 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#include <unistd.h>
12#include <sys/types.h>
13#include <zephyr/zephyr.h>
14#include <glib.h>
15#include <string.h>
16#include <stdlib.h>
17#include <sys/wait.h>
18
19#ifdef HAVE_KERBEROS_IV
20#include <kerberosIV/des.h>
21#else
22#include <openssl/des.h>
23#endif
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);
53void 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
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[])
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    {
192      fprintf(stderr, "Memory allocation error.\n");
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)
211  {
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");
215    exit(1);
216  }
217  else if(!fname)
218  {
219    exit(1);
220  }
221  else
222  {
223    if (mode == M_RANDOMIZE)
224    {
225      /* Choose a new, random key */
226/*
227      FILE *fkey = fopen(fname, "w");
228      if (!fkey)
229        printf("Could not open key file for writing: %s\n", fname);
230      else
231      {
232        char string[100];
233        fputs(fkey, string);
234        fclose(fkey);
235        }
236 */
237      fprintf(stderr, "Feature not yet implemented.\n");
238    }
239    else if (mode == M_SETKEY)
240    {
241      /* Set a new, user-entered key */
242      char newkey[MAX_KEY];
243      FILE *fkey;
244
245      if (isatty(0))
246      {
247        printf("Enter new key: ");
248        /* Really should read without echo!!! */
249      }
250      if(!fgets(newkey, MAX_KEY - 1, stdin)) {
251        fprintf(stderr, "Error reading key.\n");
252        return 1;
253      }
254
255      fkey = fopen(fname, "w");
256      if (!fkey)
257        fprintf(stderr, "Could not open key file for writing: %s\n", fname);
258      else
259      {
260        if (fputs(newkey, fkey) != strlen(newkey) || putc('\n', fkey) != '\n')
261        {
262          fprintf(stderr, "Error writing to key file.\n");
263          fclose(fkey);
264          exit(1);
265        }
266        else
267        {
268          fclose(fkey);
269          fprintf(stderr, "Key update complete.\n");
270        }
271      }
272    }
273    else
274    {
275      /* Encrypt/decrypt */
276      FILE *fkey = fopen(fname, "r");
277      if (!fkey) {
278        fprintf(stderr, "Could not open key file: %s\n", fname);
279        exit(1);
280      }
281      else
282      {
283        char keystring[MAX_KEY];
284        if(!fgets(keystring, MAX_KEY-1, fkey)) {
285          fclose(fkey);
286          fprintf(stderr, "Error reading key file.\n");
287          return 1;
288        }
289        if (mode == M_ZEPHYR_ENCRYPT || mode == M_ENCRYPT)
290          do_encrypt(keystring, (mode == M_ZEPHYR_ENCRYPT), class, instance,
291                     &zoptions, fname);
292        else
293          do_decrypt(keystring);
294        fclose(fkey);
295      }
296    }
297  }
298
299  /* Always print the **END** message if -D is specified. */
300  if (mode == M_DECRYPT)
301    printf("**END**\n");
302  return 0;
303}
304
305/* Build a space-separated string from argv from elements between start  *
306 * and end - 1.  malloc()'s the returned string. */
307char *BuildArgString(char **argv, int start, int end)
308{
309  int len = 1;
310  int i;
311  char *result;
312
313  /* Compute the length of the string.  (Plus 1 or 2) */
314  for (i = start; i < end; i++)
315    len += strlen(argv[i]) + 1;
316
317  /* Allocate memory */
318  result = (char *)malloc(len);
319  if (result)
320  {
321    /* Build the string */
322    char *ptr = result;
323    /* Start with an empty string, in case nothing is copied. */
324    *ptr = '\0';
325    /* Copy the arguments */
326    for (i = start; i < end; i++)
327    {
328      char *temp = argv[i];
329      /* Add a space, if not the first argument */
330      if (i != start)
331        *ptr++ = ' ';
332      /* Copy argv[i], leaving ptr pointing to the '\0' copied from temp */
333      while ((*ptr = *temp++))
334        ptr++;
335    }
336  }
337
338  return result;
339}
340
341#define MAX_BUFF 258
342#define MAX_SEARCH 3
343/* Find the class/instance in the .crypt-table */
344char *GetZephyrVarKeyFile(char *whoami, char *class, char *instance)
345{
346  char *keyfile = NULL;
347  char *varname[MAX_SEARCH];
348  int length[MAX_SEARCH], i;
349  char buffer[MAX_BUFF];
350  char *filename;
351  char result[MAX_SEARCH][MAX_BUFF];
352  int numsearch = 0;
353  FILE *fsearch;
354
355  memset(varname, 0, sizeof(varname));
356
357  /* Determine names to look for in .crypt-table */
358  if (instance)
359    varname[numsearch++] = g_strdup_printf("crypt-%s-%s:", (class?class:"message"), instance);
360  if (class)
361    varname[numsearch++] = g_strdup_printf("crypt-%s:", class);
362  varname[numsearch++] = g_strdup("crypt-default:");
363
364  /* Setup the result array, and determine string lengths */
365  for (i = 0; i < numsearch; i++)
366  {
367    result[i][0] = '\0';
368    length[i] = strlen(varname[i]);
369  }
370
371  /* Open~/.crypt-table */
372  filename = g_strdup_printf("%s/.crypt-table", getenv("HOME"));
373  fsearch = fopen(filename, "r");
374  if (fsearch)
375  {
376    /* Scan file for a match */
377    while (!feof(fsearch))
378    {
379      if (!fgets(buffer, MAX_BUFF - 3, fsearch)) break;
380      for (i = 0; i < numsearch; i++)
381        if (strncasecmp(varname[i], buffer, length[i]) == 0)
382        {
383          int j;
384          for (j = length[i]; buffer[j] == ' '; j++)
385            ;
386          strcpy(result[i], &buffer[j]);
387          if (*result[i])
388            if (result[i][strlen(result[i])-1] == '\n')
389              result[i][strlen(result[i])-1] = '\0';
390        }
391    }
392
393    /* Pick the "best" match found */
394    keyfile = NULL;
395    for (i = 0; i < numsearch; i++)
396      if (*result[i])
397      {
398        keyfile = result[i];
399        break;
400      }
401
402    if (keyfile == NULL)
403    {
404      fprintf(stderr, "Could not find key table entry.\n");
405    }
406    else
407    {
408      /* Prepare result to be returned */
409      char *temp = keyfile;
410      keyfile = (char *)malloc(strlen(temp) + 1);
411      if (keyfile)
412        strcpy(keyfile, temp);
413      else
414        fprintf(stderr, "Memory allocation error.\n");
415    }
416   
417    fclose(fsearch);
418  }
419  else
420    fprintf(stderr, "Could not open key table file: %s\n", filename);
421
422  for(i = 0; i < MAX_SEARCH; i++) {
423    if(varname[i] != NULL) {
424      g_free(varname[i]);
425    }
426  }
427
428  if(filename != NULL) {
429    g_free(filename);
430  }
431
432  return keyfile;
433}
434
435static pid_t zephyrpipe_pid = 0;
436
437/* Open a pipe to zwrite */
438FILE *GetZephyrPipe(char *class, char *instance, ZWRITEOPTIONS *zoptions)
439{
440  int fildes[2];
441  pid_t pid;
442  FILE *result;
443  char *argv[20];
444  int argc = 0;
445
446  if (pipe(fildes) < 0)
447    return NULL;
448  pid = fork();
449
450  if (pid < 0)
451  {
452    /* Error: clean up */
453    close(fildes[0]);
454    close(fildes[1]);
455    result = NULL;
456  }
457  else if (pid == 0)
458  {
459    /* Setup child process */
460    argv[argc++] = "zwrite";
461    argv[argc++] = "-n";     /* Always send without ping */
462    if (class)
463    {
464      argv[argc++] = "-c";
465      argv[argc++] = class;
466    }
467    if (instance)
468    {
469      argv[argc++] = "-i";
470      argv[argc++] = instance;
471    }
472    if (zoptions->flags & ZWRITE_OPT_NOAUTH)
473      argv[argc++] = "-d";
474    if (zoptions->flags & ZWRITE_OPT_QUIET)
475      argv[argc++] = "-q";
476    if (zoptions->flags & ZWRITE_OPT_VERBOSE)
477      argv[argc++] = "-v";
478    if (zoptions->flags & ZWRITE_OPT_SIGNATURE)
479    {
480      argv[argc++] = "-s";
481      argv[argc++] = zoptions->signature;
482    }
483    argv[argc++] = "-O";
484    argv[argc++] = "crypt";
485    argv[argc] = NULL;
486    close(fildes[1]);
487    if (fildes[0] != STDIN_FILENO)
488    {
489      if (dup2(fildes[0], STDIN_FILENO) != STDIN_FILENO)
490        exit(0);
491      close(fildes[0]);
492    }
493    close(fildes[0]);
494    execvp(argv[0], argv);
495    fprintf(stderr, "Exec error: could not run zwrite\n");
496    exit(0);
497  }
498  else
499  {
500    close(fildes[0]);
501    /* Create a FILE * for the zwrite pipe */
502    result = (FILE *)fdopen(fildes[1], "w");
503    zephyrpipe_pid = pid;
504  }
505
506  return result;
507}
508
509/* Close the pipe to zwrite */
510void CloseZephyrPipe(FILE *pipe)
511{
512  fclose(pipe);
513  waitpid(zephyrpipe_pid, NULL, 0);
514  zephyrpipe_pid = 0;
515}
516
517#define MAX_RESULT 2048
518
519#define BASE_CODE 70
520#define LAST_CODE (BASE_CODE + 15)
521#define OUTPUT_BLOCK_SIZE 16
522
523void block_to_ascii(unsigned char *output, FILE *outfile)
524{
525  int i;
526  for (i = 0; i < 8; i++)
527  {
528    putc(((output[i] & 0xf0) >> 4) + BASE_CODE, outfile);
529    putc( (output[i] & 0x0f)       + BASE_CODE, outfile);
530  }
531}
532
533#define MAX_LINE 128
534
535/* Encrypt stdin, with prompt if isatty, and send to stdout, or to zwrite
536   if zephyr is set. */
537int do_encrypt(char *keystring, int zephyr, char *class, char *instance,
538          ZWRITEOPTIONS *zoptions, char* keyfile)
539{
540  des_key_schedule schedule;
541  unsigned char input[8], output[8];
542  int size;
543  FILE *outfile = stdout;
544  int error = FALSE;
545  char *inbuff = NULL, *inptr;
546  int freein = FALSE;
547  int use_buffer = FALSE;
548  int num_blocks = 0, last_block_size = 0;
549
550  owl_zcrypt_string_to_schedule(keystring, schedule);
551
552  if (zephyr)
553  {
554    if (zoptions->flags & ZCRYPT_OPT_MESSAGE)
555    {
556      /* Use the -m message */
557      int length;
558      inbuff = zoptions->message;     
559      length = strlen(inbuff);
560      num_blocks = (length + 7) / 8;
561      last_block_size = ((length + 7) % 8) + 1;
562      use_buffer = TRUE;
563    }
564    else if (isatty(0))
565    {
566      /* tty input, so show the "Type your message now..." message */
567      if (zoptions->flags & ZCRYPT_OPT_IGNOREDOT)
568        printf("Type your message now.  End with the end-of-file character.\n");
569      else
570        printf("Type your message now.  End with control-D or a dot on a line by itself.\n");
571      use_buffer = TRUE;
572      if ((inptr = inbuff = (char *)malloc(MAX_RESULT)) == NULL)
573      {
574        fprintf(stderr, "Memory allocation error\n");
575        return FALSE;
576      }
577      while (inptr - inbuff < MAX_RESULT - MAX_LINE - 20)
578      {
579        if (!fgets(inptr, MAX_LINE, stdin))
580          return FALSE;
581        if (inptr[0])
582        {
583          if (inptr[0] == '.' && inptr[1] == '\n' && 
584              !(zoptions->flags & ZCRYPT_OPT_IGNOREDOT))
585          {
586            inptr[0] = '\0';
587            break;
588          }
589          else
590            inptr += strlen(inptr);
591        }
592        else
593          break;
594      }
595      num_blocks = (inptr - inbuff + 7) / 8;
596      last_block_size = ((inptr - inbuff + 7) % 8) + 1;
597      freein = TRUE;
598    }
599
600    /* if (zephyr) */
601    outfile = GetZephyrPipe(class, instance, zoptions);
602    if (!outfile)
603    {
604      fprintf(stderr, "Could not run zwrite\n");
605      if (freein && inbuff)
606        free(inbuff);
607      return FALSE;
608    }
609  }
610
611  inptr = inbuff;
612
613  /* Encrypt the input (inbuff or stdin) and send it to outfile */
614  while (TRUE)
615  {
616    if (use_buffer)
617    {
618      /* Get 8 bytes from buffer */
619      if (num_blocks > 1)
620      {
621        size = 8;
622        memcpy(input, inptr, size);
623        inptr += 8;
624        num_blocks--;
625      }
626      else if (num_blocks == 1)
627      {
628        size = last_block_size;
629        memcpy(input, inptr, size);
630        num_blocks--;
631      }
632      else
633        size = 0;
634    }
635    else
636      /* Get 8 bytes from stdin */
637      size = fread(input, 1, 8, stdin);
638
639    /* Check for EOF and pad the string to 8 chars, if needed */
640    if (size == 0)
641      break;                      /* END OF INPUT: BREAK FROM while LOOP! */
642    if (size < 8)
643      memset(input + size, 0, 8 - size);
644
645    /* Encrypt and output the block */
646    des_ecb_encrypt(&input, &output, schedule, TRUE);
647    block_to_ascii(output, outfile);
648
649    if (size < 8)
650      break;
651  }
652
653  /* Close out the output */
654  if (!error)
655    putc('\n', outfile);
656  if (zephyr)
657    CloseZephyrPipe(outfile);
658
659  /* Free the input buffer, if necessary */
660  if (freein && inbuff)
661    free(inbuff);
662
663  return !error;
664}
665
666/* Read a half-byte from stdin, skipping invalid characters.  Returns -1
667   if at EOF or file error */
668int read_ascii_nybble(void)
669{
670  char c;
671
672  while (TRUE)
673  {
674    if (fread(&c, 1, 1, stdin) == 0)
675      return -1;
676    else if (c >= BASE_CODE && c <= LAST_CODE)
677      return c - BASE_CODE;
678  }
679}
680
681/* Read both halves of the byte and return the single byte.  Returns -1
682   if at EOF or file error. */
683int read_ascii_byte(void)
684{
685  int c1, c2;
686  c1 = read_ascii_nybble();
687  if (c1 >= 0)
688  {
689    c2 = read_ascii_nybble();
690    if (c2 >= 0)
691    {
692      return c1 * 0x10 + c2;
693    }
694  }
695  return -1;
696}
697
698/* Read an 8-byte DES block from stdin */
699int read_ascii_block(unsigned char *input)
700{
701  int c;
702
703  int i;
704  for (i = 0; i < 8; i++)
705  {
706    c = read_ascii_byte();
707    if (c < 0)
708      return FALSE;
709
710    input[i] = c;
711  }
712
713  return TRUE;
714}
715
716/* Decrypt stdin */
717void do_decrypt(char *keystring)
718{
719  des_key_schedule schedule;
720  unsigned char input[8], output[8];
721
722  output[0] = '\0';    /* In case no message at all                 */
723
724  owl_zcrypt_string_to_schedule(keystring, schedule);
725
726  while (read_ascii_block(input))
727  {
728    des_ecb_encrypt(&input, &output, schedule, FALSE);
729    printf("%s", output);
730  }
731
732  if (!output[0] || output[strlen((const char*)output) - 1] != '\n')
733      printf("\n");
734}
Note: See TracBrowser for help on using the repository browser.