source: zcrypt.c @ 9a4077c

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