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