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