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