-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrc4filter.c
235 lines (187 loc) · 5.3 KB
/
rc4filter.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/* vim: set ts=4 sw=4 noexpandtab: */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/mman.h>
#include <stdbool.h>
#include "rc4.h"
#include "cmdlineparse.h"
#define DEF_BUFSIZE (size_t)(1 << 12) /* page size? */
static unsigned char passphrase[256] = {0};
static size_t passlen = 0;
static bool have_pass = false;
static char *input_file = NULL;
static char *output_file = NULL;
static size_t bufsize = DEF_BUFSIZE;
static inline void err_exit(const char *str)
{
perror(str);
exit(EXIT_FAILURE);
}
/* Apparently getpass() is deprecated, and this should be portable */
static void read_password_terminal(
const char *prompt, unsigned char *password, size_t *len)
{
struct termios oldterm, newterm;
ssize_t n = 0;
ssize_t plen = strlen(prompt);
size_t i = 0;
char c;
int fd;
if(!isatty(fileno(stdin))) {
if((fd = open("/dev/tty", O_RDWR)) < 0)
err_exit("Open terminal");
} else {
fd = fileno(stdin);
}
/* Must write this way since we may be a terminal w/o a stdio handle */
do {
n += write(fd, prompt, plen - n);
} while(n < plen);
tcgetattr(fd, &oldterm);
newterm = oldterm;
/*setting the approriate bit in the termios struct*/
newterm.c_lflag &= ~(ECHO);
newterm.c_lflag |= ECHONL;
/*setting the new bits*/
tcsetattr(fd, TCSANOW, &newterm);
while (i < 255) {
n = read(fd, &c, 1);
if(n < 1)
err_exit("Reading from terminal");
if(c == '\n')
break;
password[i++] = c;
}
password[i] = '\0';
*len = i;
/* resetting our old terminal settings */
tcsetattr(fd, TCSANOW, &oldterm);
if(fd != fileno(stdin))
close(fd);
}
static void read_passfile(const char *filename)
{
FILE *fp = fopen(filename, "r");
if(fp == NULL) {
fprintf(stderr, "Error, cannot open pass-file %s\n", filename);
exit(EXIT_FAILURE);
}
passlen = fread(passphrase, 1, 256, fp);
if(passlen == 0) {
fputs("WARNING: empty passphrase encountered\n", stderr);
} else if(ferror(fp)) {
fputs("ERROR: file-error reading from pass-file\n", stderr);
exit(EXIT_FAILURE);
} else if (!feof(fp)) {
fputs("WARNING: only the first 256 bytes of pass-file are used\n", stderr);
}
fclose(fp);
}
/* Set the configuration options above from cmdline */
static void initialize_options(int argc, char *argv[])
{
int c;
/* Use getopt_long here because with POSIX feature-tets-macro set we don't
* permute option strings, but we want to because we're lazy
*/
while((c=getopt_long(argc, argv, "hp:f:b:", NULL, NULL)) != -1) {
switch(c) {
case 'p':
if(optarg == NULL) {
fputs("Error, no password specified in -p option\n", stderr);
exit(EXIT_FAILURE);
}
passlen = strlen(optarg) < 255 ? strlen(optarg) : 255;
memmove(passphrase, optarg, passlen);
have_pass = true;
break;
case 'f':
read_passfile(optarg);
have_pass = true;
break;
case 'b':
bufsize = parse_num(c);
break;
case 'h':
fprintf(stderr,
"Usage: %s [OPTION] [INPUT] [OUTPUT]\n\
Options:\n\
-p passphrase to use, if not given prompt from user on stdin\n\
-f pass-file to use, read contents of file and user as passphrase\n\
-b block-size to use (default %zu)\n\n\
Arguments:\n\
INPUT optional input file, if not given or given as '-', read stdin\n\
OUTPUT optional output file, if not given write to stdout\n\n\
Notes:\n\
If reading from stdin, the user will be asked to provide a password\n\
from the terminal, so unless -p or -f is specified, the program needs \n\
a controlling terminal or it will throw an error.\n\
", argv[0], DEF_BUFSIZE);
exit(EXIT_SUCCESS);
case '?':
exit(EXIT_FAILURE);
default:
abort();
}
}
if(argc > optind)
input_file = !strcmp(argv[optind], "-") ? NULL : argv[optind];
if(argc > optind + 1)
output_file = !strcmp(argv[optind + 1], "-") ? NULL : argv[optind + 1];
if(argc > optind + 2) {
fputs("ERROR: too many arguments specified\n", stderr);
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[])
{
FILE *fp_in, *fp_out;
unsigned char *buf;
struct rc4_ctx ctx;
size_t nread;
if((buf = malloc(bufsize * sizeof(unsigned char))) == NULL) {
fprintf(stderr, "ERROR: allocating %ld bytes for buffer!?", bufsize);
return 1;
}
if(mlock(passphrase, sizeof(passphrase)) != 0) {
perror("memlock passphrase");
return 1;
}
initialize_options(argc, argv);
if(!have_pass)
read_password_terminal("Password: ", passphrase, &passlen);
if(passlen == 0) {
fputs("WARNING: zero-length password, using 1 null byte\n", stderr);
passlen += 1;
}
rc4_init_key(&ctx, passphrase, passlen);
memset(passphrase, 0xff, sizeof(passphrase));
munlock(passphrase, sizeof(passphrase));
if(input_file == NULL) {
fp_in = stdin;
} else if((fp_in = fopen(input_file, "r")) == NULL) {
fprintf(stderr, "ERROR: cannot open inupt file '%s'\n", input_file);
return 1;
}
if(output_file == NULL) {
fp_out = stdout;
} else if((fp_out = fopen(output_file, "w")) == NULL) {
fprintf(stderr, "ERROR: cannot open output file '%s'\n", output_file);
return 1;
}
while((nread = fread(buf, 1, bufsize, fp_in)) != 0) {
rc4_xor_stream(&ctx, buf, nread);
if(fwrite(buf, 1, nread, fp_out) != nread)
err_exit("write-error to output file");
}
fclose(fp_in);
fclose(fp_out);
free(buf);
return 0;
}