chiark / gitweb /
Small tweaks. Support no-network configuration option, and rearrange
[become] / src / crypt.c
1 /* -*-c-*-
2  *
3  * $Id: crypt.c,v 1.4 1998/01/12 16:45:55 mdw Exp $
4  *
5  * Cryptographic transfer of `become' requests
6  *
7  * (c) 1998 EBI
8  */
9
10 /*----- Licensing 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 Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------*
30  *
31  * $Log: crypt.c,v $
32  * Revision 1.4  1998/01/12 16:45:55  mdw
33  * Fix copyright date.
34  *
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  *
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
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>
65 #include <sys/time.h>
66 #include <unistd.h>
67 #include <syslog.h>
68 #include <fcntl.h>
69
70 /* --- Local headers --- */
71
72 #include "become.h"
73 #include "blowfish.h"
74 #include "config.h"
75 #include "crypt.h"
76 #include "icrypt.h"
77 #include "md5.h"
78 #include "noise.h"
79 #include "rand.h"
80 #include "tx.h"
81 #include "utils.h"
82
83 /*----- Magic numbers -----------------------------------------------------*/
84
85 #define crypt__timeError 60             /* Seconds error to permit */
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
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
101 static void crypt__sessionKey(const char *seedfile, unsigned char *k,
102                               unsigned char *sk, unsigned char *iv)
103 {
104   FILE *fp;                             /* File handle for reading */
105   struct flock l;
106   int ok = 1;
107
108   /* --- Open the random seed file --- *
109    *
110    * If I can't manage that, create a new one.
111    */
112
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();
118   }
119
120   /* --- Lock the seed file against concurrency problems --- */
121
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));
128
129     /* --- Now read the file, and launder the seed --- */
130
131   if (ok)
132     rand_read(fp);
133
134   /* --- Encrypt the pool using the secret key --- */
135
136   {
137     icrypt_job j;
138     icrypt_init(&j, k, BLOWFISH_KEYSIZE, 0);
139     rand_encrypt(&j);
140     burn(j);
141   }
142
143   /* --- Generate the session key and IV --- */
144
145   noise_acquire();
146   rand_extract(sk, BLOWFISH_KEYSIZE);
147   rand_extract(iv, BLOWFISH_BLKSIZE);
148
149   IF_TRACING(TRACE_CRYPTO,
150     traceblk(TRACE_CRYPTO, "crypto: session key:", sk, BLOWFISH_KEYSIZE);
151     traceblk(TRACE_CRYPTO, "crypto: initialisation vector:",
152              iv, BLOWFISH_BLKSIZE);
153   );
154
155   /* --- Write the seed back --- */
156
157   rewind(fp);
158   rand_write(fp);
159   fclose(fp);
160
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
179 void 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
185   buff[crq_cryptType] = cryptType_blowfish;
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
191   /* --- Now generate session keys and things --- */
192
193   crypt__sessionKey(file_RANDSEED, k, sk, buff + crq_iv);
194   memcpy(buff + crq_session, sk, BLOWFISH_KEYSIZE);
195
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
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.
207    */
208
209   {
210     icrypt_job j;
211     unsigned char *p;
212     unsigned u;
213     md5 md;
214     unsigned char qk[BLOWFISH_KEYSIZE];
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++) {
220       u = rand(); *p = u ^ (u >> 8);
221     }
222
223     /* --- Now make the junk a whole lot harder to predict --- */
224
225     p = buff + crq_cmd;
226     md5_init(&md); md5_buffer(&md, p, CMDLEN_MAX); md5_final(&md, qk);
227     icrypt_init(&j, qk, BLOWFISH_KEYSIZE, 0);
228     icrypt_encrypt(&j, p, p, CMDLEN_MAX);
229     burn(j); burn(qk); burn(md);
230
231     /* --- Copy the string into here --- */
232
233     strcpy((char *)buff + crq_cmd, rq->cmd);
234   }
235
236   /* --- Checksum the finished data --- */
237
238   {
239     md5 md;
240     unsigned char mdbuf[MD5_HASHSIZE];
241
242     md5_init(&md);
243     md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
244     md5_final(&md, mdbuf);
245     memcpy(buff + crq_check, mdbuf, 4);
246     burn(md); burn(mdbuf);
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
260     T( traceblk(TRACE_CRYPTO, "crypto: plaintext request:",
261                 buff, crq_size); )
262
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
281     icrypt_encrypt(&j, buff + crq_cipher,
282                    buff + crq_cipher, crq_size - crq_cipher);
283     burn(j);
284
285     T( traceblk(TRACE_CRYPTO, "crypto: ciphertext request:",
286                 buff, crq_size); )
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
303 int 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
310     if (buff[crq_cryptType] != cryptType_blowfish)
311       return (0);
312   }
313
314   {
315     /* --- First things first: decrypt the block --- */
316
317     icrypt_job j;
318
319     T( traceblk(TRACE_CRYPTO, "crypto: ciphertext request:",
320                 buff, crq_size); )
321
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
342     icrypt_decrypt(&j, buff + crq_cipher,
343                    buff + crq_cipher, crq_size - crq_cipher);
344     icrypt_saveIV(&j, rpl + crp_iv);
345
346     T( traceblk(TRACE_CRYPTO, "crypto: plaintext request:",
347                 buff, crq_size); )
348
349     memset(buff + crq_session, 0, BLOWFISH_KEYSIZE); /* Burn, baby, burn */
350     burn(j);
351   }
352
353   {
354     /* --- Check the validity of the data therein --- */
355
356     md5 md;
357     unsigned char mdbuf[MD5_HASHSIZE];
358
359     md5_init(&md);
360     md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
361     md5_final(&md, mdbuf);
362     if (memcmp(mdbuf, buff + crq_check, 4) != 0) {
363       syslog(LOG_INFO, "packet rejected: bad checksum");
364       T( trace(TRACE_CRYPTO, "crypto: bad checksum on incoming request"); )
365       return (0);
366     }
367     burn(md); burn(mdbuf);
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");
386       T( trace(TRACE_CRYPTO, "crypto: bad time on incoming request"); )
387       return (0);
388     }
389     memcpy(rpl + crp_time, buff + crq_time, 8);
390   }
391
392   /* --- Done --- */
393
394   T( trace(TRACE_CRYPTO, "crypto: valid request received"); )
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
409 void 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;
421     unsigned char mdbuf[MD5_HASHSIZE];
422
423     md5_init(&md);
424     md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
425     md5_final(&md, mdbuf);
426     memcpy(buff + crp_check, mdbuf, 4);
427     burn(md); burn(mdbuf);
428   }
429
430   {
431     /* --- Encrypt the buffer --- */
432
433     icrypt_job j;
434
435     T( traceblk(TRACE_CRYPTO, "crypto: plaintext reply:", buff, crp_size); )
436
437     icrypt_init(&j, sk, BLOWFISH_KEYSIZE, buff + crp_iv);
438     icrypt_encrypt(&j, buff + crp_cipher,
439                    buff + crp_cipher, crp_size - crp_cipher);
440     burn(j);
441
442     T( traceblk(TRACE_CRYPTO, "crypto: ciphertext reply:", buff, crp_size); )
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
458 int 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;
465
466     T( traceblk(TRACE_CRYPTO, "crypto: ciphertext reply:", buff, crp_size); )
467
468     icrypt_init(&j, sk, BLOWFISH_KEYSIZE, buff + crp_iv);
469     icrypt_decrypt(&j, buff + crp_cipher,
470                    buff + crp_cipher, crp_size - crp_cipher);
471     burn(j);
472
473     T( traceblk(TRACE_CRYPTO, "crypto: plaintext reply:", buff, crp_size); )
474   }
475
476   {
477     /* --- Check validity --- */
478
479     md5 md;
480     unsigned char mdbuf[MD5_HASHSIZE];
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);
487     md5_final(&md, mdbuf);
488     if (memcmp(buff + crp_check, mdbuf, 4) != 0) {
489       syslog(LOG_INFO, "reply rejected: bad checksum");
490       T( trace(TRACE_CRYPTO, "crypto: bad checksum on reply"); )
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");
499       T( trace(TRACE_CRYPTO, "crypto: bad id on reply"); )
500       return (-1);
501     }
502   }
503
504   /* --- Return the value --- */
505
506   T( trace(TRACE_CRYPTO, "crypto: valid reply received"); )
507   return (buff[crp_answer]);
508 }
509
510 /*----- Test rig ----------------------------------------------------------*/
511
512 #ifdef TEST_RIG
513
514 int main(int argc, char *argv[])
515 {
516   unsigned char buff[8];
517   unsigned char sk[BLOWFISH_KEYSIZE], k[BLOWFISH_KEYSIZE];
518   FILE *fp;
519
520   ego(argv[0]);
521   traceon(stdout, TRACE_CRYPTO);
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);
529   crypt__sessionKey(argv[2], k, sk, buff);
530   return (0);
531 }
532
533 #endif
534
535 /*----- That's all, folks -------------------------------------------------*/