1 /* program: shallot (based on the popular onionhash program by Bebop)
3 * purpose: brute-force customized SHA1-hashes of RSA keys for Tor's
5 * license: OSI-approved MIT License
6 * credits: Bebop, for onionhash, which credits the following:
7 * - Plasmoid (The Hacker's Choice)
8 * - Roger Dingledine (Tor Project)
9 * - Nick Mathewson (Tor Project)
10 * - Eric Young (OpenSSL Project)
12 * I would also like to thank the following testers/benchmarkers:
14 * - Cheeze- (Linux, OS X)
15 * - Tas (Linux, OS X, OpenBSD)
17 * - cybernetseraph (OpenBSD)
19 * Special thanks to nickm for some debugging assistance!
20 * Extra-special thanks to Tas for his patience and assistance
21 * in getting shallot to build on OpenBSD, OS X, and Linux
23 * contact: mention bugs to <`Orum@OFTC or `Orum@ORC>
28 * - figure out a way to allow for -p w/o a pattern being supplied
29 * - allow -m to be used with -f (use file for status output) [v0.0.3]
42 // Linux specific headers
45 #include <string.h> // included with something else on *BSD
50 // BSD specific headers
51 #include <sys/param.h> // needed on OpenBSD
52 #include <sys/sysctl.h>
59 void terminate(int signum
) { // Ctrl-C/kill handler
60 error(X_SGNL_INT_TERM
);
63 int main(int argc
, char *argv
[]) { // onions are fun, here we go
64 signal(SIGTERM
, terminate
); // always let people kill
66 if(argc
< 2) // not enough arguments
69 // set up our initial values
70 uint8_t daemon
= 0, optimum
= 0;
71 uint32_t threads
= 1, x
= 1;
73 elim
= DEFAULT_E_LIMIT
;
79 int mib
[2] = { CTL_HW
, HW_NCPU
}; // how
80 size_t size
= sizeof(threads
); // easy
81 if(sysctl(mib
, 2, &threads
, &size
, NULL
, 0)) // BSD
82 error(X_SYSCTL_FAILED
); // is
84 #elif defined(LINUX_PORT) // Oh no! We're on linux... :(
85 // ...even *Windows 95* (gasp!) has a better way of doing this...
86 // TODO: move this to linux.c
87 char cpuinfo
[CPUINFO_BUF_SIZE
] = "";
88 int fd
= open(CPUINFO_PATH
, O_RDONLY
);
91 error(X_BAD_FILE_DESC
);
93 threads
--; // reset threads to 0
95 ssize_t tmp
= 1; // must not initially be 0!
100 tmp
= read(fd
, &cpuinfo
[r
], CPUINFO_BUF_SIZE
- r
); // fill the buffer
102 if(tmp
< 0) // something went wrong
103 error(X_ABNORMAL_READ
);
105 r
+= tmp
; // add how much we read
107 if(r
< CPUINFO_BUF_SIZE
)
110 threads
+= parse_cpuinfo(&cpuinfo
[0], (uint16_t)r
, &used
);
111 r
-= used
; // subtract what we parsed
113 memmove(&cpuinfo
[0], &cpuinfo
[used
], r
);
116 close(fd
); // TODO: add error handling! (is there any point?)
118 if(!threads
) { // This is needed for Liunx/ARM (do not remove!)
119 printf("WARNING: No CPUs detected. Defaulting to 1 thread... "
120 "(or manually specify thread count with -t)\n");
124 #elif defined(GENERIC)
125 printf("WARNING: Threads will default to 1 unless specified with -t\n");
128 for(; x
< argc
- 1; x
++) { // options parsing
129 if(argv
[x
][0] != '-') {
130 fprintf(stderr
, "Error: Options must start with '-'\n");
134 for(; argv
[x
][y
] != '\0'; y
++) {
137 case 'd': { // daemonize
141 case 'm': { // monitor
145 case 'o': { // prime optimization
149 case 'p': { // pattern help
153 case 'f': { // file <file>
154 if((argv
[x
][y
+ 1] != '\0') || (x
+ 1 > argc
)) {
155 fprintf(stderr
, "Error: -f format is '-f <file>'\n");
162 case 't': { // threads
163 if((argv
[x
][y
+ 1] != '\0') || (x
+ 1 > argc
)) {
164 fprintf(stderr
, "Error: -t format is '-t threads'\n");
167 threads
= strtoul(argv
[x
+ 1], NULL
, 0);
171 case 'e': { // e limit
172 if((argv
[x
][y
+ 1] != '\0') || (x
+ 1 > argc
)) {
173 fprintf(stderr
, "Error: -e format is '-e limit'\n");
176 elim
= strtoull(argv
[x
+ 1], NULL
, 0);
180 default: { // unrecognized
181 fprintf(stderr
, "Error: Unrecognized option - '%c'\n", argv
[x
][y
]);
183 break; // redundant... but safe :)
187 x
++; //skip the next param
193 // now for our sanity checks
195 error(X_INVALID_THRDS
);
198 error(X_EXCLUSIVE_OPT
);
200 if(!(elim
& 1) || (elim
< RSA_PK_EXPONENT
) || (elim
> MAXIMUM_E_LIMIT
))
201 error(X_INVALID_E_LIM
);
204 error(X_NEED_FILE_OUT
);
206 // compile regular expression from argument
207 char *pattern
= argv
[argc
- 1];
210 error(X_REGEX_INVALID
);
212 regex
= malloc(REGEX_COMP_LMAX
);
214 if(regcomp(regex
, pattern
, REG_EXTENDED
| REG_NOSUB
))
215 error(X_REGEX_COMPILE
);
218 umask(077); // remove permissions to be safe
222 (freopen(file
, "w", stdout
) == NULL
) ||
223 (freopen(file
, "w", stderr
) == NULL
)
224 ) error(X_FILE_OPEN_ERR
);
227 if(daemon
&& (getppid() != 1)) { // daemonize if we should
230 if(pid
< 0) // fork failed
231 error(X_DAEMON_FAILED
);
233 if(pid
) // exit on the parent process
236 if(setsid() < 0) // get a new SID
237 error(X_DAEMON_FAILED
);
239 if(chdir("/") < 0) // cd to root
240 error(X_DAEMON_FAILED
);
243 if (freopen("/dev/null", "r", stdin
) == NULL
)
244 error(X_FILE_OPEN_ERR
);
246 // ignore certain signals
247 signal(SIGCHLD
, SIG_IGN
);
248 signal(SIGTSTP
, SIG_IGN
);
249 signal(SIGTTOU
, SIG_IGN
);
250 signal(SIGTTIN
, SIG_IGN
);
251 signal(SIGHUP
, SIG_IGN
);
253 } else signal(SIGINT
, terminate
); // die on CTRL-C
257 // create our threads for 2+ cores
258 for(x
= 1; x
< threads
; x
++) {
260 if(pthread_create(&thrd
, NULL
, worker
, &optimum
))
261 error(X_THREAD_CREATE
);
265 // TODO: when support is added for -mv, put a message here
266 if(pthread_create(&thrd
, NULL
, monitor_proc
, NULL
))
267 error(X_THREAD_CREATE
);
270 worker(&optimum
); // use main thread for brute forcing too
272 if(pthread_self() != lucky_thread
) { // be safe and avoid EDEADLK
274 pthread_join(lucky_thread
, NULL
); // wait for the lucky thread to exit