86ed6058b11ba61c366c16408d18a8b30544e7bb
[become] / src / crypt.c
1 /* -*-c-*-
2 *
3 * $Id: crypt.c,v 1.1 1997/07/21 13:47:51 mdw Exp $
4 *
5 * Cryptographic transfer of `become' requests
6 *
7 * (c) 1997 EBI
8 */
9
10 /*----- Licencing notice --------------------------------------------------*
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
25 * along with `become'; if not, write to the Free Software
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 */
28
29 /*----- Revision history --------------------------------------------------*
30 *
31 * $Log: crypt.c,v $
32 * Revision 1.1 1997/07/21 13:47:51 mdw
33 * Initial revision
34 *
35 */
36
37 /*----- Header files ------------------------------------------------------*/
38
39 /* --- ANSI headers --- */
40
41 #include <ctype.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <time.h>
47
48 /* --- Unix headers --- */
49
50 #include <sys/types.h>
51 #include <unistd.h>
52 #include <syslog.h>
53 #include <fcntl.h>
54
55 /* --- Local headers --- */
56
57 #include "config.h"
58 #include "crypt.h"
59 #include "icrypt.h"
60 #include "idea.h"
61 #include "md5.h"
62 #include "tx.h"
63 #include "utils.h"
64
65 /*----- Magic numbers -----------------------------------------------------*/
66
67 #define crypt__timeError 15 /* Seconds error to permit */
68 #define crypt__seedBits 512 /* Number of random seed bits */
69
70 /*----- Dump a block of data ----------------------------------------------*/
71
72 /* --- @crypt__dump@ --- *
73 *
74 * Arguments: @char *p@ = a string to display at the top of the message
75 * @unsigned char *buf@ = pointer to a buffer to display
76 * @size_t sz@ = size of the buffer
77 * @FILE *fp@ = file to dump on
78 *
79 * Returns: --
80 *
81 * Use: Dumps a block of data to the terminal. This allows a
82 * programmer to examine the traffic between client and server,
83 * and possibly locate bugs.
84 */
85
86 #ifndef NDEBUG
87
88 static void crypt__dump(const char *p, const unsigned char *buf,
89 int sz, FILE *fp)
90 {
91 int i;
92 fprintf(stderr,"+++ %s\n",p);
93 while (sz>0)
94 {
95 fprintf(stderr,"+++ ");
96 for (i=0;i<8;i++)
97 {
98 if (i<sz)
99 fprintf(stderr,"%02x ",buf[i] & 0xff);
100 else
101 fputs("** ",stderr);
102 }
103 fputs(": ",stderr);
104 for (i=0;i<8;i++)
105 putc(i<sz ? (isprint(buf[i]) ? buf[i] : '.') : '*',stderr);
106 putc('\n',stderr);
107 buf+=8;
108 sz-=8;
109 }
110 putc('\n',stderr);
111 }
112
113 #else
114
115 #define crypt__dump(p, buf, sz, fp) ((void)0)
116
117 #endif
118
119 /*----- Main code ---------------------------------------------------------*/
120
121 /* --- @crypt__sessionKey@ --- *
122 *
123 * Arguments: @const char *seedfile@ = pointer to name of seed file
124 * @unsigned char *k@ = our secret key
125 * @time_t t@ = the current time
126 * @pid_t pid@ = our process id
127 * @unsigned *sk@ = where to store the session key
128 * @unsigned char *iv@ = where to store the IV
129 *
130 * Returns: ---
131 *
132 * Use: Decides on a random session key and initialisation vector.
133 */
134
135 static void crypt__sessionKey(const char *seedfile, unsigned char *k,
136 time_t t, pid_t pid,
137 unsigned char *sk, unsigned char *iv)
138 {
139 FILE *fp; /* File handle for reading */
140 unsigned char s[crypt__seedBits / 8]; /* Random seed block */
141 unsigned char b[4 + 4];
142 /* For building the hash block */
143
144 /* --- Read the old seed in --- *
145 *
146 * Interlock with other processes locking this file.
147 */
148
149 {
150 #ifndef TEST_RIG
151 struct flock l;
152 #endif
153
154 if ((fp = fopen(seedfile, "r+")) == 0)
155 die("couldn't open random seed file `%s': %s",
156 seedfile, strerror(errno));
157
158 #ifndef TEST_RIG
159 l.l_type = F_WRLCK;
160 l.l_whence = SEEK_SET;
161 l.l_start = 0;
162 l.l_len = 0;
163 if (fcntl(fileno(fp), F_SETLKW, &l) < 0)
164 die("couldn't lock random number file: %s", strerror(errno));
165 #endif
166
167 tx_getBits(s, crypt__seedBits, fp);
168 }
169
170 crypt__dump("Initial seed", s, crypt__seedBits / 8, stdout);
171
172 /* --- Build a block of data to hash --- */
173
174 store32(b + 0, t);
175 store32(b + 4, pid);
176
177 /* --- Work out the session key --- */
178
179 {
180 md5 md;
181
182 md5_init(&md);
183 md5_buffer(&md, s, sizeof(s));
184 md5_buffer(&md, b, sizeof(b));
185 md5_buffer(&md, k, IDEA_KEYSIZE);
186 md5_final(&md, sk);
187
188 crypt__dump("Session key", sk, 128 / 8, stdout);
189 burn(md);
190 }
191
192 /* --- Build the new seed and write it back again --- */
193
194 if (crypt__seedBits > 128) {
195 unsigned char tb[MD5_HASHSIZE];
196 int i;
197
198 memcpy(tb, s + crypt__seedBits / 8 - sizeof(tb), sizeof(tb));
199 memmove(s + sizeof(tb), s, crypt__seedBits / 8 - sizeof(tb));
200 memcpy(s, tb, sizeof(tb));
201 for (i = 0; i < sizeof(sk); i++)
202 s[i] ^= sk[i];
203 burn(tb);
204 }
205
206 /* --- Take the seed we have and hash it again to get an IV --- */
207
208 {
209 unsigned char mdv[MD5_HASHSIZE];
210 md5 md;
211
212 md5_init(&md);
213 md5_buffer(&md, b, sizeof(b));
214 md5_buffer(&md, k, IDEA_KEYSIZE);
215 md5_buffer(&md, s, sizeof(s));
216 md5_final(&md, mdv);
217 memcpy(iv, mdv, IDEA_BLKSIZE);
218 crypt__dump("IV", iv, IDEA_BLKSIZE, stdout);
219 burn(md); burn(mdv);
220 }
221
222
223 /* --- Lock the file again --- *
224 *
225 * We're closing the file after we've finished, so we don't need to
226 * unlock it afterwards.
227 */
228
229 crypt__dump("Final seed", s, crypt__seedBits / 8, stdout);
230
231 rewind(fp);
232 tx_putBits(s, crypt__seedBits, fp);
233 fclose(fp);
234
235 /* --- Destroy sensitive data --- */
236
237 burn(b); burn(s);
238 }
239
240 /* --- @crypt_packRequest@ --- *
241 *
242 * Arguments: @request *rq@ = pointer to request block
243 * @unsigned char *buff@ = pointer to a buffer
244 * @time_t t@ = the current time
245 * @pid_t pid@ = my process ID
246 * @unsigned char *k@ = pointer to 128-bit key
247 * @unsigned char *sk@ = where to put the session key
248 *
249 * Returns: ---
250 *
251 * Use: Packs a request block into a buffer. The buffer should have
252 * space for at least @crq_size@ bytes. The buffer comes back
253 * encrypted and ready to send.
254 */
255
256 void crypt_packRequest(request *rq, unsigned char *buff,
257 time_t t, pid_t pid,
258 unsigned char *k, unsigned char *sk)
259 {
260 /* --- First, build the easy stuff in the block --- */
261
262 buff[crq_cryptType] = cryptType_idea;
263 store32(buff + crq_time, t);
264 store32(buff + crq_pid, pid);
265 store32(buff + crq_from, rq->from);
266 store32(buff + crq_to, rq->to);
267
268 /* --- The string causes a few problems --- *
269 *
270 * There's a good chance that the string will be a good deal shorter than
271 * the space allowed for it. This will probably mean lots of zeroes, and a
272 * very easy known-plaintext job for a potential attacker. (An early
273 * version of this code used @strncpy@ which is even worse!)
274 *
275 * I'll fill the block with random (from @rand@(3) -- nothing too
276 * elaborate) and then encrypt it using IDEA in CFB mode, using the first
277 * few bytes as the key. This should provide a sufficiently unpredictable
278 * background for the block.
279 */
280
281 {
282 icrypt_job j;
283 unsigned char *p;
284 unsigned u;
285
286 /* --- Initialise the buffer with junk --- */
287
288 srand((unsigned int)(t ^ pid)); /* Seed the (bad) RNG */
289 for (p = buff + crq_cmd; p < buff + crq_cmd + CMDLEN_MAX; p++) {
290 u = rand();
291 *p = u ^ (u >> 8);
292 }
293
294 /* --- Now make the junk a whole lot harder to predict --- */
295
296 p = buff + crq_cmd;
297 icrypt_init(&j, p, 0);
298 icrypt_encrypt(&j, p, p, CMDLEN_MAX);
299 burn(j);
300
301 /* --- Copy the string into here --- */
302
303 strcpy((char *)buff + crq_cmd, rq->cmd);
304 }
305
306 /* --- Generate a session key --- */
307
308 {
309 crypt__sessionKey(file_RANDSEED, k, t, pid, sk, buff + crq_iv);
310 memcpy(buff + crq_session, sk, IDEA_KEYSIZE);
311 }
312
313 /* --- Checksum the finished data --- */
314
315 {
316 md5 md;
317 unsigned char mdv[MD5_HASHSIZE];
318
319 md5_init(&md);
320 md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
321 md5_final(&md, mdv);
322 memcpy(buff + crq_check, mdv, 4);
323 burn(md); burn(mdv);
324 }
325
326 /* --- Encrypt the block --- *
327 *
328 * First, encrypt the session key using the master key. Since the session
329 * key is effectively random, this makes cracking the master key much
330 * harder. The rest of the block is then encrypted with the session key,
331 * using the IV left over from encrypting the session key.
332 */
333
334 {
335 icrypt_job j;
336
337 crypt__dump("request, before encryption", buff, crq_size, stdout);
338
339 icrypt_init(&j, k, buff + crq_iv);
340 icrypt_encrypt(&j, buff + crq_session, buff + crq_session, IDEA_KEYSIZE);
341 icrypt_reset(&j, sk, 0);
342 icrypt_encrypt(&j, buff + crq_cipher,
343 buff + crq_cipher, crq_size - crq_cipher);
344 burn(j);
345
346 crypt__dump("request, after encryption", buff, crq_size, stdout);
347 }
348 }
349
350 /* --- @crypt_unpackRequest@ --- *
351 *
352 * Arguments: @reqest *rq@ = pointer to destination request block
353 * @unsigned char *buff@ = pointer to source buffer
354 * @unsigned char *k@ = pointer to encryption key
355 * @unsigned char *sk@ = pointer to where to store session key
356 * @unsigned char *rpl@ = where to start building reply
357 *
358 * Returns: Nonzero if it was decrypted OK
359 *
360 * Use: Decrypts and unpacks a request buffer.
361 */
362
363 int crypt_unpackRequest(request *rq, unsigned char *buff,
364 unsigned char *k, unsigned char *sk,
365 unsigned char *rpl)
366 {
367 {
368 /* --- Check the encryption format --- */
369
370 if (buff[crq_cryptType] != cryptType_idea)
371 return (0);
372 }
373
374 {
375 /* --- First things first: decrypt the block --- */
376
377 icrypt_job j;
378
379 crypt__dump("request, before decryption", buff, crq_size, stdout);
380
381 icrypt_init(&j, k, buff + crq_iv);
382 icrypt_decrypt(&j, buff + crq_session, buff + crq_session, IDEA_KEYSIZE);
383 memcpy(sk, buff + crq_session, IDEA_KEYSIZE);
384 icrypt_reset(&j, sk, 0);
385 icrypt_decrypt(&j, buff + crq_cipher,
386 buff + crq_cipher, crq_size - crq_cipher);
387 icrypt_saveIV(&j, rpl + crp_iv);
388
389 memset(buff + crq_session, 0, IDEA_KEYSIZE); /* Burn, baby, burn */
390 burn(j);
391
392 crypt__dump("request, after decryption", buff, crq_size, stdout);
393 }
394
395 {
396 /* --- Check the validity of the data therein --- */
397
398 md5 md;
399 unsigned char mdv[MD5_HASHSIZE];
400
401 md5_init(&md);
402 md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
403 md5_final(&md, mdv);
404 if (memcmp(mdv, buff + crq_check, 4) != 0) {
405 syslog(LOG_INFO, "packet rejected: bad checksum");
406 return (0);
407 }
408 burn(md); burn(mdv);
409 }
410
411 {
412 /* --- Extract fields from the block --- */
413
414 rq->from = load32(buff + crq_from);
415 rq->to = load32(buff + crq_to);
416 memcpy(rq->cmd, buff + crq_cmd, CMDLEN_MAX);
417 }
418
419 {
420 /* --- Fill in bits of the reply block --- */
421
422 long t = (long)time(0);
423 long u = (long)load32(buff + crq_time);
424
425 if (t - u > crypt__timeError || u - t > crypt__timeError) {
426 syslog(LOG_INFO, "packet rejected: bad time");
427 return (0);
428 }
429 memcpy(rpl + crp_time, buff + crq_time, 8);
430 }
431
432 /* --- Done --- */
433
434 return (1);
435 }
436
437 /* --- @crypt_packReply@ --- *
438 *
439 * Arguments: @char *buff@ = pointer to reply block
440 * @unsigned char *sk@ = pointer to session key
441 * @int answer@ = yes or no
442 *
443 * Returns: ---
444 *
445 * Use: Packs and encrypts a reply block.
446 */
447
448 void crypt_packReply(unsigned char *buff, unsigned char *sk, int answer)
449 {
450 {
451 /* --- Store the answer --- */
452
453 buff[crp_answer] = (answer != 0);
454 }
455
456 {
457 /* --- Build the checksum --- */
458
459 md5 md;
460 unsigned char mdv[MD5_HASHSIZE];
461
462 md5_init(&md);
463 md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
464 md5_final(&md, mdv);
465 memcpy(buff + crp_check, mdv, 4);
466 burn(md); burn(mdv);
467 }
468
469 {
470 /* --- Encrypt the buffer --- */
471
472 icrypt_job j;
473 icrypt_init(&j, sk, buff + crp_iv);
474 crypt__dump("reply, before encryption", buff, crp_size, stdout);
475 icrypt_encrypt(&j, buff + crp_cipher,
476 buff + crp_cipher, crp_size - crp_cipher);
477 crypt__dump("reply, after encryption", buff, crp_size, stdout);
478 burn(j);
479 }
480 }
481
482 /* --- @crypt_unpackReply@ --- *
483 *
484 * Arguments: @unsigned char *buff@ = pointer to reply buffer
485 * @unsigned char *sk@ = pointer to session key
486 * @time_t t@ = time at which request was sent
487 * @pid_t pid@ = my process ID
488 *
489 * Returns: >0 if request granted, zero if denied, <0 if reply rejected
490 *
491 * Use: Unpacks a reply block, and informs the caller of the outcome.
492 */
493
494 int crypt_unpackReply(unsigned char *buff, unsigned char *sk,
495 time_t t, pid_t pid)
496 {
497 {
498 /* --- Decrypt my reply block --- */
499
500 icrypt_job j;
501 icrypt_init(&j, sk, buff + crp_iv);
502 crypt__dump("reply, before decryption", buff, crp_size, stdout);
503 icrypt_decrypt(&j, buff + crp_cipher,
504 buff + crp_cipher, crp_size - crp_cipher);
505 crypt__dump("reply, after decryption", buff, crp_size, stdout);
506 burn(j);
507 }
508
509 {
510 /* --- Check validity --- */
511
512 md5 md;
513 unsigned char mdv[MD5_HASHSIZE];
514 char b[8];
515
516 /* --- Check the checksum --- */
517
518 md5_init(&md);
519 md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
520 md5_final(&md, mdv);
521 if (memcmp(buff + crp_check, mdv, 4) != 0) {
522 syslog(LOG_INFO, "reply rejected: bad checksum");
523 return (-1);
524 }
525
526 /* --- Check the identifier --- */
527
528 store32(b + 0, t); store32(b + 4, pid);
529 if (memcmp(b, buff + crp_time, sizeof(b)) != 0) {
530 syslog(LOG_INFO, "reply rejected: bad identification marker");
531 return (-1);
532 }
533 }
534
535 /* --- Return the value --- */
536
537 return (buff[crp_answer]);
538 }
539
540 /*----- Test rig ----------------------------------------------------------*/
541
542 #ifdef TEST_RIG
543
544 int main(int argc, char *argv[])
545 {
546 time_t t = time(0);
547 pid_t pid = getpid();
548 unsigned char buff[8];
549 unsigned char sk[IDEA_KEYSIZE], k[IDEA_KEYSIZE];
550 FILE *fp;
551
552 ego(argv[0]);
553 if (argc < 3)
554 die("bad args");
555 fp = fopen(argv[1], "r");
556 if (!fp)
557 die("fopen: %s", strerror(errno));
558 tx_getBits(k, 128, fp);
559 fclose(fp);
560 crypt__sessionKey(argv[2], k, t, pid, sk, buff);
561 return (0);
562 }
563
564 #endif
565
566 /*----- That's all, folks -------------------------------------------------*/