Imported Upstream version 0.4b37
[debian/dump] / rmt / cipher.c
1 #include <sys/types.h>
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <syslog.h>
7 #include <strings.h>
8 #include <errno.h>
9 #include <openssl/evp.h>
10
11 /*
12  * Encrypt or decrypt buf, returning a pointer to the transformed data,
13  * or NULL on error.
14  * The returned data is the same size as the input data,
15  * which means we must turn off cipher padding, and require that buflen
16  * be a multiple of the cipher block size (8 for Blowfish).
17  * To keep things simple, the return value is a malloc'd
18  * buffer that is overwritten on each call.
19  *
20  * Ken Lalonde <ken@globalremit.com>, 2003
21  */
22 char *
23 cipher(char *buf, int buflen, int do_encrypt)
24 {
25         static EVP_CIPHER_CTX ctx;
26         static char *out = NULL;        /* return value, grown as necessary */
27         static int outlen = 0;
28         static int init = 0, which, blocksize;
29         int n;
30
31         if (!init) {
32                 static const EVP_CIPHER *cipher;
33                 unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
34                 // Read key from $HOME/.ermt.key
35                 char *keyfile = ".ermt.key";
36                 unsigned char buf[128];
37                 FILE *fp;
38                 int i;
39
40                 openlog("ermt", LOG_PID, LOG_DAEMON);
41                 // Not needed: OpenSSL_add_all_algorithms();
42                 // Not needed: ERR_load_crypto_strings();
43                 cipher = EVP_bf_cbc();  // or: EVP_aes_{128,192,256}_cbc()
44                 // We want the ability to decrypt the output
45                 // using "openssl enc -d -kfile K -nopad -nosalt", which
46                 // means we must read the key file the same way
47                 // openssl does.  But be careful: if the key contains
48                 // \0 or \r or \n, the effective size will be reduced.
49                 if ((fp = fopen(keyfile, "r")) == NULL) {
50                         syslog(LOG_ERR, "Can't open key file %s: %m", keyfile);
51                         return NULL;
52                 }
53                 buf[0] = '\0';
54                 fgets(buf, sizeof buf, fp);
55                 fclose(fp);
56                 i = strlen(buf);
57                 if ((i > 0) &&
58                         ((buf[i-1] == '\n') || (buf[i-1] == '\r')))
59                         buf[--i]='\0';
60                 if ((i > 0) &&
61                         ((buf[i-1] == '\n') || (buf[i-1] == '\r')))
62                         buf[--i]='\0';
63                 if (i < 1) {
64                         syslog(LOG_ERR, "zero length key");
65                         errno = EINVAL;
66                         return NULL;
67                 }
68                 EVP_BytesToKey(cipher, EVP_md5(), NULL,
69                         buf, strlen(buf), 1, key, iv);
70                 EVP_CIPHER_CTX_init(&ctx);
71                 EVP_CipherInit_ex(&ctx, cipher, NULL, key, iv, do_encrypt);
72                 EVP_CIPHER_CTX_set_padding(&ctx, 0);    // -nopad
73                 OPENSSL_cleanse(buf, sizeof buf);
74                 OPENSSL_cleanse(key, sizeof key);
75                 OPENSSL_cleanse(iv, sizeof iv);
76                 blocksize = EVP_CIPHER_CTX_block_size(&ctx);
77                 which = do_encrypt;
78                 init = 1;
79         }
80         if (which != do_encrypt) {
81                 syslog(LOG_ERR, "Cannot switch modes");
82                 errno = EINVAL;
83                 return NULL;
84         }
85         if ((buflen % blocksize) != 0) {
86                 syslog(LOG_ERR, "Buffer size is not a multiple of cipher block size");
87                 errno = EINVAL;
88                 return NULL;
89         }
90         if (outlen < buflen+blocksize) {
91                 outlen = (buflen+blocksize) * 2;
92                 out = realloc(out, outlen);
93         }
94         if (!EVP_CipherUpdate(&ctx, out, &n, buf, buflen)) {
95                 syslog(LOG_ERR, "EVP_CipherUpdate failed");
96                 errno = EINVAL;
97                 return NULL;
98         }
99         if (n != buflen) {
100                 syslog(LOG_ERR, "EVP_CipherUpdate: %d != %d", n, buflen);
101                 errno = EINVAL;
102                 return NULL;
103         }
104         // assert(ctx->buf_len == 0);
105         return out;
106 }
107
108 /* Decrypt stdin to stdout, and exit */
109 int
110 decrypt()
111 {
112         char buf[8*1024], *cp;
113         int n;
114
115         while ((n = fread(buf, 1, sizeof buf, stdin)) > 0) {
116                 if ((cp = cipher(buf, n, 0)) == NULL) {
117                         fprintf(stderr, "ermt: Error decoding input; see daemon.err syslog\n");
118                         exit(1);
119                 }
120                 fwrite(cp, 1, n, stdout);
121         }
122         if (ferror(stdin)) {
123                 perror("ermt: stdin: read");
124                 exit(1);
125         }
126         if (fflush(stdout)) {
127                 perror("ermt: stdout: write");
128                 exit(1);
129         }
130         exit(0);
131 }