chiark / gitweb /
Make it build\!
[become] / src / crypt.c
CommitLineData
c4f2d992 1/* -*-c-*-
2 *
bce14ae6 3 * $Id: crypt.c,v 1.5 1998/06/18 15:08:49 mdw Exp $
c4f2d992 4 *
5 * Cryptographic transfer of `become' requests
6 *
c758e654 7 * (c) 1998 EBI
c4f2d992 8 */
9
03f996bd 10/*----- Licensing notice --------------------------------------------------*
c4f2d992 11 *
12 * This file is part of `become'
13 *
14 * `Become' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * `Become' is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
03f996bd 25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
c4f2d992 27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: crypt.c,v $
bce14ae6 32 * Revision 1.5 1998/06/18 15:08:49 mdw
33 * Paranoia: set close-on-exec flag for seed file.
34 *
c758e654 35 * Revision 1.4 1998/01/12 16:45:55 mdw
36 * Fix copyright date.
37 *
9e5602f0 38 * Revision 1.3 1997/09/26 09:14:58 mdw
39 * Merged blowfish branch into trunk.
40 *
41 * Revision 1.2.2.1 1997/09/26 09:08:02 mdw
42 * Use the Blowfish encryption algorithm instead of IDEA. This is partly
43 * because I prefer Blowfish (without any particularly strong evidence) but
44 * mainly because IDEA is patented and Blowfish isn't.
45 *
03f996bd 46 * Revision 1.2 1997/08/04 10:24:21 mdw
47 * Sources placed under CVS control.
48 *
49 * Revision 1.1 1997/07/21 13:47:51 mdw
c4f2d992 50 * Initial revision
51 *
52 */
53
54/*----- Header files ------------------------------------------------------*/
55
56/* --- ANSI headers --- */
57
58#include <ctype.h>
59#include <errno.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
63#include <time.h>
64
65/* --- Unix headers --- */
66
67#include <sys/types.h>
03f996bd 68#include <sys/time.h>
c4f2d992 69#include <unistd.h>
70#include <syslog.h>
71#include <fcntl.h>
72
73/* --- Local headers --- */
74
03f996bd 75#include "become.h"
9e5602f0 76#include "blowfish.h"
c4f2d992 77#include "config.h"
78#include "crypt.h"
79#include "icrypt.h"
c4f2d992 80#include "md5.h"
03f996bd 81#include "noise.h"
82#include "rand.h"
c4f2d992 83#include "tx.h"
84#include "utils.h"
85
86/*----- Magic numbers -----------------------------------------------------*/
87
03f996bd 88#define crypt__timeError 60 /* Seconds error to permit */
c4f2d992 89
90/*----- Main code ---------------------------------------------------------*/
91
92/* --- @crypt__sessionKey@ --- *
93 *
94 * Arguments: @const char *seedfile@ = pointer to name of seed file
95 * @unsigned char *k@ = our secret key
c4f2d992 96 * @unsigned *sk@ = where to store the session key
97 * @unsigned char *iv@ = where to store the IV
98 *
99 * Returns: ---
100 *
101 * Use: Decides on a random session key and initialisation vector.
102 */
103
104static void crypt__sessionKey(const char *seedfile, unsigned char *k,
c4f2d992 105 unsigned char *sk, unsigned char *iv)
106{
107 FILE *fp; /* File handle for reading */
03f996bd 108 struct flock l;
109 int ok = 1;
c4f2d992 110
03f996bd 111 /* --- Open the random seed file --- *
c4f2d992 112 *
03f996bd 113 * If I can't manage that, create a new one.
c4f2d992 114 */
115
03f996bd 116 if ((fp = fopen(seedfile, "r+")) == 0) {
117 ok = 0;
118 if ((fp = fopen(seedfile, "w+")) == 0)
119 die("can't create random number file: %s", strerror(errno));
120 rand_clear();
bce14ae6 121 }
122 if (fcntl(fileno(fp), F_SETFD, 1) < 0) {
123 die("can't set close-on-exec for random number file: %s",
124 strerror(errno));
c4f2d992 125 }
126
03f996bd 127 /* --- Lock the seed file against concurrency problems --- */
c4f2d992 128
03f996bd 129 l.l_type = F_WRLCK;
130 l.l_whence = SEEK_SET;
131 l.l_start = 0;
132 l.l_len = 0;
133 if (fcntl(fileno(fp), F_SETLKW, &l) < 0)
134 die("can't lock random number file: %s", strerror(errno));
c4f2d992 135
03f996bd 136 /* --- Now read the file, and launder the seed --- */
c4f2d992 137
03f996bd 138 if (ok)
139 rand_read(fp);
c4f2d992 140
03f996bd 141 /* --- Encrypt the pool using the secret key --- */
c4f2d992 142
143 {
03f996bd 144 icrypt_job j;
9e5602f0 145 icrypt_init(&j, k, BLOWFISH_KEYSIZE, 0);
03f996bd 146 rand_encrypt(&j);
147 burn(j);
c4f2d992 148 }
149
03f996bd 150 /* --- Generate the session key and IV --- */
c4f2d992 151
03f996bd 152 noise_acquire();
9e5602f0 153 rand_extract(sk, BLOWFISH_KEYSIZE);
154 rand_extract(iv, BLOWFISH_BLKSIZE);
03f996bd 155
156 IF_TRACING(TRACE_CRYPTO,
9e5602f0 157 traceblk(TRACE_CRYPTO, "crypto: session key:", sk, BLOWFISH_KEYSIZE);
03f996bd 158 traceblk(TRACE_CRYPTO, "crypto: initialisation vector:",
9e5602f0 159 iv, BLOWFISH_BLKSIZE);
03f996bd 160 );
c4f2d992 161
03f996bd 162 /* --- Write the seed back --- */
c4f2d992 163
164 rewind(fp);
03f996bd 165 rand_write(fp);
c4f2d992 166 fclose(fp);
167
c4f2d992 168}
169
170/* --- @crypt_packRequest@ --- *
171 *
172 * Arguments: @request *rq@ = pointer to request block
173 * @unsigned char *buff@ = pointer to a buffer
174 * @time_t t@ = the current time
175 * @pid_t pid@ = my process ID
176 * @unsigned char *k@ = pointer to 128-bit key
177 * @unsigned char *sk@ = where to put the session key
178 *
179 * Returns: ---
180 *
181 * Use: Packs a request block into a buffer. The buffer should have
182 * space for at least @crq_size@ bytes. The buffer comes back
183 * encrypted and ready to send.
184 */
185
186void crypt_packRequest(request *rq, unsigned char *buff,
187 time_t t, pid_t pid,
188 unsigned char *k, unsigned char *sk)
189{
190 /* --- First, build the easy stuff in the block --- */
191
9e5602f0 192 buff[crq_cryptType] = cryptType_blowfish;
c4f2d992 193 store32(buff + crq_time, t);
194 store32(buff + crq_pid, pid);
195 store32(buff + crq_from, rq->from);
196 store32(buff + crq_to, rq->to);
197
03f996bd 198 /* --- Now generate session keys and things --- */
199
200 crypt__sessionKey(file_RANDSEED, k, sk, buff + crq_iv);
9e5602f0 201 memcpy(buff + crq_session, sk, BLOWFISH_KEYSIZE);
03f996bd 202
c4f2d992 203 /* --- The string causes a few problems --- *
204 *
205 * There's a good chance that the string will be a good deal shorter than
206 * the space allowed for it. This will probably mean lots of zeroes, and a
207 * very easy known-plaintext job for a potential attacker. (An early
208 * version of this code used @strncpy@ which is even worse!)
209 *
210 * I'll fill the block with random (from @rand@(3) -- nothing too
9e5602f0 211 * elaborate) and then encrypt it using Blowfish in CFB mode, using the
212 * first few bytes as the key. This should provide a sufficiently
213 * unpredictable background for the block.
c4f2d992 214 */
215
216 {
217 icrypt_job j;
218 unsigned char *p;
219 unsigned u;
03f996bd 220 md5 md;
9e5602f0 221 unsigned char qk[BLOWFISH_KEYSIZE];
c4f2d992 222
223 /* --- Initialise the buffer with junk --- */
224
225 srand((unsigned int)(t ^ pid)); /* Seed the (bad) RNG */
226 for (p = buff + crq_cmd; p < buff + crq_cmd + CMDLEN_MAX; p++) {
03f996bd 227 u = rand(); *p = u ^ (u >> 8);
c4f2d992 228 }
229
230 /* --- Now make the junk a whole lot harder to predict --- */
231
232 p = buff + crq_cmd;
03f996bd 233 md5_init(&md); md5_buffer(&md, p, CMDLEN_MAX); md5_final(&md, qk);
9e5602f0 234 icrypt_init(&j, qk, BLOWFISH_KEYSIZE, 0);
235 icrypt_encrypt(&j, p, p, CMDLEN_MAX);
03f996bd 236 burn(j); burn(qk); burn(md);
c4f2d992 237
238 /* --- Copy the string into here --- */
239
240 strcpy((char *)buff + crq_cmd, rq->cmd);
241 }
242
c4f2d992 243 /* --- Checksum the finished data --- */
244
245 {
246 md5 md;
03f996bd 247 unsigned char mdbuf[MD5_HASHSIZE];
c4f2d992 248
249 md5_init(&md);
250 md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
03f996bd 251 md5_final(&md, mdbuf);
252 memcpy(buff + crq_check, mdbuf, 4);
253 burn(md); burn(mdbuf);
c4f2d992 254 }
255
256 /* --- Encrypt the block --- *
257 *
258 * First, encrypt the session key using the master key. Since the session
259 * key is effectively random, this makes cracking the master key much
260 * harder. The rest of the block is then encrypted with the session key,
261 * using the IV left over from encrypting the session key.
262 */
263
264 {
265 icrypt_job j;
266
03f996bd 267 T( traceblk(TRACE_CRYPTO, "crypto: plaintext request:",
268 buff, crq_size); )
c4f2d992 269
9e5602f0 270 T( traceblk(TRACE_CRYPTO, "crypto: master key:", k, BLOWFISH_KEYSIZE); )
271 T( traceblk(TRACE_CRYPTO, "crypto: initial iv:",
272 buff + crq_iv, BLOWFISH_BLKSIZE); )
273 T( traceblk(TRACE_CRYPTO, "crypto: session key:",
274 sk, BLOWFISH_KEYSIZE); )
275
276 icrypt_init(&j, k, BLOWFISH_KEYSIZE, buff + crq_iv);
277
278 icrypt_encrypt(&j, buff + crq_session,
279 buff + crq_session, BLOWFISH_KEYSIZE);
280 T( traceblk(TRACE_CRYPTO, "crypto: encrypted session key:",
281 buff + crq_session, BLOWFISH_KEYSIZE); )
282
283 icrypt_reset(&j, sk, BLOWFISH_KEYSIZE, 0);
284
285 T( traceblk(TRACE_CRYPTO, "crypto: partial iv:",
286 j.iv, BLOWFISH_BLKSIZE); )
287
c4f2d992 288 icrypt_encrypt(&j, buff + crq_cipher,
289 buff + crq_cipher, crq_size - crq_cipher);
290 burn(j);
291
03f996bd 292 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext request:",
293 buff, crq_size); )
c4f2d992 294 }
295}
296
297/* --- @crypt_unpackRequest@ --- *
298 *
299 * Arguments: @reqest *rq@ = pointer to destination request block
300 * @unsigned char *buff@ = pointer to source buffer
301 * @unsigned char *k@ = pointer to encryption key
302 * @unsigned char *sk@ = pointer to where to store session key
303 * @unsigned char *rpl@ = where to start building reply
304 *
305 * Returns: Nonzero if it was decrypted OK
306 *
307 * Use: Decrypts and unpacks a request buffer.
308 */
309
310int crypt_unpackRequest(request *rq, unsigned char *buff,
311 unsigned char *k, unsigned char *sk,
312 unsigned char *rpl)
313{
314 {
315 /* --- Check the encryption format --- */
316
9e5602f0 317 if (buff[crq_cryptType] != cryptType_blowfish)
c4f2d992 318 return (0);
319 }
320
321 {
322 /* --- First things first: decrypt the block --- */
323
324 icrypt_job j;
325
03f996bd 326 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext request:",
327 buff, crq_size); )
c4f2d992 328
9e5602f0 329 T( traceblk(TRACE_CRYPTO, "crypto: master key:", k, BLOWFISH_KEYSIZE); )
330 T( traceblk(TRACE_CRYPTO, "crypto: initial iv:",
331 buff + crq_iv, BLOWFISH_BLKSIZE); )
332
333 icrypt_init(&j, k, BLOWFISH_KEYSIZE, buff + crq_iv);
334 T( traceblk(TRACE_CRYPTO, "crypto: job block:", &j, sizeof(j)); )
335
336 T( traceblk(TRACE_CRYPTO, "crypto: encrypted session key:",
337 buff + crq_session, BLOWFISH_KEYSIZE); )
338 icrypt_decrypt(&j, buff + crq_session,
339 buff + crq_session, BLOWFISH_KEYSIZE);
340 memcpy(sk, buff + crq_session, BLOWFISH_KEYSIZE);
341 T( traceblk(TRACE_CRYPTO, "crypto: session key:",
342 sk, BLOWFISH_KEYSIZE); )
343
344 icrypt_reset(&j, sk, BLOWFISH_KEYSIZE, 0);
345
346 T( traceblk(TRACE_CRYPTO, "crypto: partial iv:",
347 j.iv, BLOWFISH_BLKSIZE); )
348
c4f2d992 349 icrypt_decrypt(&j, buff + crq_cipher,
350 buff + crq_cipher, crq_size - crq_cipher);
351 icrypt_saveIV(&j, rpl + crp_iv);
352
03f996bd 353 T( traceblk(TRACE_CRYPTO, "crypto: plaintext request:",
354 buff, crq_size); )
9e5602f0 355
356 memset(buff + crq_session, 0, BLOWFISH_KEYSIZE); /* Burn, baby, burn */
357 burn(j);
c4f2d992 358 }
359
360 {
361 /* --- Check the validity of the data therein --- */
362
363 md5 md;
03f996bd 364 unsigned char mdbuf[MD5_HASHSIZE];
c4f2d992 365
366 md5_init(&md);
367 md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
03f996bd 368 md5_final(&md, mdbuf);
369 if (memcmp(mdbuf, buff + crq_check, 4) != 0) {
c4f2d992 370 syslog(LOG_INFO, "packet rejected: bad checksum");
03f996bd 371 T( trace(TRACE_CRYPTO, "crypto: bad checksum on incoming request"); )
c4f2d992 372 return (0);
373 }
03f996bd 374 burn(md); burn(mdbuf);
c4f2d992 375 }
376
377 {
378 /* --- Extract fields from the block --- */
379
380 rq->from = load32(buff + crq_from);
381 rq->to = load32(buff + crq_to);
382 memcpy(rq->cmd, buff + crq_cmd, CMDLEN_MAX);
383 }
384
385 {
386 /* --- Fill in bits of the reply block --- */
387
388 long t = (long)time(0);
389 long u = (long)load32(buff + crq_time);
390
391 if (t - u > crypt__timeError || u - t > crypt__timeError) {
392 syslog(LOG_INFO, "packet rejected: bad time");
03f996bd 393 T( trace(TRACE_CRYPTO, "crypto: bad time on incoming request"); )
c4f2d992 394 return (0);
395 }
396 memcpy(rpl + crp_time, buff + crq_time, 8);
397 }
398
399 /* --- Done --- */
400
03f996bd 401 T( trace(TRACE_CRYPTO, "crypto: valid request received"); )
c4f2d992 402 return (1);
403}
404
405/* --- @crypt_packReply@ --- *
406 *
407 * Arguments: @char *buff@ = pointer to reply block
408 * @unsigned char *sk@ = pointer to session key
409 * @int answer@ = yes or no
410 *
411 * Returns: ---
412 *
413 * Use: Packs and encrypts a reply block.
414 */
415
416void crypt_packReply(unsigned char *buff, unsigned char *sk, int answer)
417{
418 {
419 /* --- Store the answer --- */
420
421 buff[crp_answer] = (answer != 0);
422 }
423
424 {
425 /* --- Build the checksum --- */
426
427 md5 md;
03f996bd 428 unsigned char mdbuf[MD5_HASHSIZE];
c4f2d992 429
430 md5_init(&md);
431 md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
03f996bd 432 md5_final(&md, mdbuf);
433 memcpy(buff + crp_check, mdbuf, 4);
434 burn(md); burn(mdbuf);
c4f2d992 435 }
436
437 {
438 /* --- Encrypt the buffer --- */
439
440 icrypt_job j;
03f996bd 441
442 T( traceblk(TRACE_CRYPTO, "crypto: plaintext reply:", buff, crp_size); )
443
9e5602f0 444 icrypt_init(&j, sk, BLOWFISH_KEYSIZE, buff + crp_iv);
c4f2d992 445 icrypt_encrypt(&j, buff + crp_cipher,
446 buff + crp_cipher, crp_size - crp_cipher);
c4f2d992 447 burn(j);
03f996bd 448
449 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext reply:", buff, crp_size); )
c4f2d992 450 }
451}
452
453/* --- @crypt_unpackReply@ --- *
454 *
455 * Arguments: @unsigned char *buff@ = pointer to reply buffer
456 * @unsigned char *sk@ = pointer to session key
457 * @time_t t@ = time at which request was sent
458 * @pid_t pid@ = my process ID
459 *
460 * Returns: >0 if request granted, zero if denied, <0 if reply rejected
461 *
462 * Use: Unpacks a reply block, and informs the caller of the outcome.
463 */
464
465int crypt_unpackReply(unsigned char *buff, unsigned char *sk,
466 time_t t, pid_t pid)
467{
468 {
469 /* --- Decrypt my reply block --- */
470
471 icrypt_job j;
03f996bd 472
473 T( traceblk(TRACE_CRYPTO, "crypto: ciphertext reply:", buff, crp_size); )
474
9e5602f0 475 icrypt_init(&j, sk, BLOWFISH_KEYSIZE, buff + crp_iv);
c4f2d992 476 icrypt_decrypt(&j, buff + crp_cipher,
477 buff + crp_cipher, crp_size - crp_cipher);
c4f2d992 478 burn(j);
03f996bd 479
480 T( traceblk(TRACE_CRYPTO, "crypto: plaintext reply:", buff, crp_size); )
c4f2d992 481 }
482
483 {
484 /* --- Check validity --- */
485
486 md5 md;
03f996bd 487 unsigned char mdbuf[MD5_HASHSIZE];
c4f2d992 488 char b[8];
489
490 /* --- Check the checksum --- */
491
492 md5_init(&md);
493 md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
03f996bd 494 md5_final(&md, mdbuf);
495 if (memcmp(buff + crp_check, mdbuf, 4) != 0) {
c4f2d992 496 syslog(LOG_INFO, "reply rejected: bad checksum");
03f996bd 497 T( trace(TRACE_CRYPTO, "crypto: bad checksum on reply"); )
c4f2d992 498 return (-1);
499 }
500
501 /* --- Check the identifier --- */
502
503 store32(b + 0, t); store32(b + 4, pid);
504 if (memcmp(b, buff + crp_time, sizeof(b)) != 0) {
505 syslog(LOG_INFO, "reply rejected: bad identification marker");
03f996bd 506 T( trace(TRACE_CRYPTO, "crypto: bad id on reply"); )
c4f2d992 507 return (-1);
508 }
509 }
510
511 /* --- Return the value --- */
512
03f996bd 513 T( trace(TRACE_CRYPTO, "crypto: valid reply received"); )
c4f2d992 514 return (buff[crp_answer]);
515}
516
517/*----- Test rig ----------------------------------------------------------*/
518
519#ifdef TEST_RIG
520
521int main(int argc, char *argv[])
522{
c4f2d992 523 unsigned char buff[8];
9e5602f0 524 unsigned char sk[BLOWFISH_KEYSIZE], k[BLOWFISH_KEYSIZE];
c4f2d992 525 FILE *fp;
526
527 ego(argv[0]);
03f996bd 528 traceon(stdout, TRACE_CRYPTO);
c4f2d992 529 if (argc < 3)
530 die("bad args");
531 fp = fopen(argv[1], "r");
532 if (!fp)
533 die("fopen: %s", strerror(errno));
534 tx_getBits(k, 128, fp);
535 fclose(fp);
03f996bd 536 crypt__sessionKey(argv[2], k, sk, buff);
c4f2d992 537 return (0);
538}
539
540#endif
541
542/*----- That's all, folks -------------------------------------------------*/