cleaning up
[strongswan.git] / src / openac / openac.c
1 /**
2 * @file openac.c
3 *
4 * @brief Generation of X.509 attribute certificates.
5 *
6 */
7
8 /*
9 * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler
10 * Copyright (C) 2004,2007 Andreas Steffen
11 * Hochschule fuer Technik Rapperswil, Switzerland
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <syslog.h>
28 #include <unistd.h>
29 #include <getopt.h>
30 #include <ctype.h>
31 #include <time.h>
32 #include <gmp.h>
33
34 #include <debug.h>
35 #include <asn1/asn1.h>
36 #include <asn1/ttodata.h>
37 #include <crypto/ac.h>
38 #include <crypto/ietf_attr_list.h>
39 #include <utils/optionsfrom.h>
40
41 #ifdef INTEGRITY_TEST
42 #include <fips/fips.h>
43 #include <fips_signature.h>
44 #endif /* INTEGRITY_TEST */
45
46 #include "build.h"
47
48 #define OPENAC_PATH IPSEC_CONFDIR "/openac"
49 #define OPENAC_SERIAL IPSEC_CONFDIR "/openac/serial"
50
51 static void usage(const char *mess)
52 {
53 if (mess != NULL && *mess != '\0')
54 {
55 fprintf(stderr, "%s\n", mess);
56 }
57 fprintf(stderr, "Usage: openac"
58 " [--help]"
59 " [--version]"
60 " [--optionsfrom <filename>]"
61 " [--quiet]"
62 " \\\n\t"
63 " [--debug <level 0..4>]"
64 " \\\n\t"
65 " [--days <days>]"
66 " [--hours <hours>]"
67 " \\\n\t"
68 " [--startdate <YYYYMMDDHHMMSSZ>]"
69 " [--enddate <YYYYMMDDHHMMSSZ>]"
70 " \\\n\t"
71 " --cert <certfile>"
72 " --key <keyfile>"
73 " [--password <password>]"
74 " \\\n\t"
75 " --usercert <certfile>"
76 " --groups <attr1,attr2,..>"
77 " --out <filename>"
78 "\n"
79 );
80 }
81
82
83 /**
84 * convert a chunk into a multi-precision integer
85 */
86 static void chunk_to_mpz(chunk_t chunk, mpz_t number)
87 {
88 mpz_import(number, chunk.len, 1, 1, 1, 0, chunk.ptr);
89 }
90
91 /**
92 * convert a multi-precision integer into a chunk
93 */
94 static chunk_t mpz_to_chunk(mpz_t number)
95 {
96 chunk_t chunk;
97
98 chunk.len = 1 + mpz_sizeinbase(number, 2)/BITS_PER_BYTE;
99 chunk.ptr = mpz_export(NULL, NULL, 1, chunk.len, 1, 0, number);
100 return chunk;
101 }
102
103 /**
104 * read the last serial number from file
105 */
106 static chunk_t read_serial(void)
107 {
108 mpz_t number;
109
110 char buf[BUF_LEN], buf1[BUF_LEN];
111 chunk_t last_serial = { buf1, BUF_LEN};
112 chunk_t serial;
113
114 FILE *fd = fopen(OPENAC_SERIAL, "r");
115
116 /* last serial number defaults to 0 */
117 *last_serial.ptr = 0x00;
118 last_serial.len = 1;
119
120 if (fd)
121 {
122 if (fscanf(fd, "%s", buf))
123 {
124 err_t ugh = ttodata(buf, 0, 16, last_serial.ptr, BUF_LEN, &last_serial.len);
125
126 if (ugh != NULL)
127 {
128 DBG1(" error reading serial number from %s: %s",
129 OPENAC_SERIAL, ugh);
130 }
131 }
132 fclose(fd);
133 }
134 else
135 {
136 DBG1(" file '%s' does not exist yet - serial number set to 01", OPENAC_SERIAL);
137 }
138
139 /**
140 * conversion of read serial number to a multiprecision integer
141 * and incrementing it by one
142 * and representing it as a two's complement octet string
143 */
144 mpz_init(number);
145 chunk_to_mpz(last_serial, number);
146 mpz_add_ui(number, number, 0x01);
147 serial = mpz_to_chunk(number);
148 mpz_clear(number);
149
150 return serial;
151 }
152
153 /**
154 * write back the last serial number to file
155 */
156 static void write_serial(chunk_t serial)
157 {
158 FILE *fd = fopen(OPENAC_SERIAL, "w");
159
160 if (fd)
161 {
162 DBG1(" serial number is %#B", &serial);
163 fprintf(fd, "%#B\n", &serial);
164 fclose(fd);
165 }
166 else
167 {
168 DBG1(" could not open file '%s' for writing", OPENAC_SERIAL);
169 }
170 }
171
172 /**
173 * global variables accessible by both main() and build.c
174 */
175 x509_t *usercert = NULL;
176 x509_t *signercert = NULL;
177
178 linked_list_t *groups = NULL;
179 rsa_private_key_t *signerkey = NULL;
180
181 time_t notBefore = UNDEFINED_TIME;
182 time_t notAfter = UNDEFINED_TIME;
183
184 chunk_t serial;
185
186 static int debug_level = 1;
187 static bool stderr_quiet = FALSE;
188 /**
189 * openac dbg function
190 */
191 static void openac_dbg(int level, char *fmt, ...)
192 {
193 int priority = LOG_INFO;
194 va_list args;
195
196 if (level <= debug_level)
197 {
198 va_start(args, fmt);
199 if (!stderr_quiet)
200 {
201 vfprintf(stderr, fmt, args);
202 fprintf(stderr, "\n");
203 }
204 vsyslog(priority, fmt, args);
205 va_end(args);
206 }
207 }
208
209 void (*dbg) (int level, char *fmt, ...) = openac_dbg;
210
211 /**
212 * openac main program
213 */
214 int main(int argc, char **argv)
215 {
216 char *keyfile = NULL;
217 char *certfile = NULL;
218 char *usercertfile = NULL;
219 char *outfile = NULL;
220 char buf[BUF_LEN];
221
222 chunk_t passphrase = { buf, 0 };
223 chunk_t attr_cert = chunk_empty;
224 x509ac_t *ac = NULL;
225
226 const time_t default_validity = 24*3600; /* 24 hours */
227 time_t validity = 0;
228 int status = 1;
229
230 passphrase.ptr[0] = '\0';
231 groups = linked_list_create();
232
233 openlog("openac", 0, LOG_AUTHPRIV);
234
235 /* handle arguments */
236 for (;;)
237 {
238 static const struct option long_opts[] = {
239 /* name, has_arg, flag, val */
240 { "help", no_argument, NULL, 'h' },
241 { "version", no_argument, NULL, 'v' },
242 { "optionsfrom", required_argument, NULL, '+' },
243 { "quiet", no_argument, NULL, 'q' },
244 { "cert", required_argument, NULL, 'c' },
245 { "key", required_argument, NULL, 'k' },
246 { "password", required_argument, NULL, 'p' },
247 { "usercert", required_argument, NULL, 'u' },
248 { "groups", required_argument, NULL, 'g' },
249 { "days", required_argument, NULL, 'D' },
250 { "hours", required_argument, NULL, 'H' },
251 { "startdate", required_argument, NULL, 'S' },
252 { "enddate", required_argument, NULL, 'E' },
253 { "out", required_argument, NULL, 'o' },
254 { "debug", required_argument, NULL, 'd' },
255 { 0,0,0,0 }
256 };
257
258 int c = getopt_long(argc, argv, "hv+:qc:k:p;u:g:D:H:S:E:o:d:", long_opts, NULL);
259
260 /* Note: "breaking" from case terminates loop */
261 switch (c)
262 {
263 case EOF: /* end of flags */
264 break;
265
266 case 0: /* long option already handled */
267 continue;
268
269 case ':': /* diagnostic already printed by getopt_long */
270 case '?': /* diagnostic already printed by getopt_long */
271 case 'h': /* --help */
272 usage(NULL);
273 status = 1;
274 goto end;
275
276 case 'v': /* --version */
277 printf("openac (strongSwan %s)\n", VERSION);
278 status = 0;
279 goto end;
280
281 case '+': /* --optionsfrom <filename> */
282 {
283 char path[BUF_LEN];
284
285 if (*optarg == '/') /* absolute pathname */
286 {
287 strncpy(path, optarg, BUF_LEN);
288 }
289 else /* relative pathname */
290 {
291 snprintf(path, BUF_LEN, "%s/%s", OPENAC_PATH, optarg);
292 }
293 optionsfrom(path, &argc, &argv, optind, stderr);
294 /* does not return on error */
295 }
296 continue;
297
298 case 'q': /* --quiet */
299 stderr_quiet = TRUE;
300 continue;
301
302 case 'c': /* --cert */
303 certfile = optarg;
304 continue;
305
306 case 'k': /* --key */
307 keyfile = optarg;
308 continue;
309
310 case 'p': /* --key */
311 if (strlen(optarg) > BUF_LEN)
312 {
313 usage("passphrase too long");
314 goto end;
315 }
316 strncpy(passphrase.ptr, optarg, BUF_LEN);
317 passphrase.len = min(strlen(optarg), BUF_LEN);
318 continue;
319
320 case 'u': /* --usercert */
321 usercertfile = optarg;
322 continue;
323
324 case 'g': /* --groups */
325 ietfAttr_list_create_from_string(optarg, groups);
326 continue;
327
328 case 'D': /* --days */
329 if (optarg == NULL || !isdigit(optarg[0]))
330 {
331 usage("missing number of days");
332 goto end;
333 }
334 else
335 {
336 char *endptr;
337 long days = strtol(optarg, &endptr, 0);
338
339 if (*endptr != '\0' || endptr == optarg || days <= 0)
340 {
341 usage("<days> must be a positive number");
342 goto end;
343 }
344 validity += 24*3600*days;
345 }
346 continue;
347
348 case 'H': /* --hours */
349 if (optarg == NULL || !isdigit(optarg[0]))
350 {
351 usage("missing number of hours");
352 goto end;
353 }
354 else
355 {
356 char *endptr;
357 long hours = strtol(optarg, &endptr, 0);
358
359 if (*endptr != '\0' || endptr == optarg || hours <= 0)
360 {
361 usage("<hours> must be a positive number");
362 goto end;
363 }
364 validity += 3600*hours;
365 }
366 continue;
367
368 case 'S': /* --startdate */
369 if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z')
370 {
371 usage("date format must be YYYYMMDDHHMMSSZ");
372 goto end;
373 }
374 else
375 {
376 chunk_t date = { optarg, 15 };
377
378 notBefore = asn1totime(&date, ASN1_GENERALIZEDTIME);
379 }
380 continue;
381
382 case 'E': /* --enddate */
383 if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z')
384 {
385 usage("date format must be YYYYMMDDHHMMSSZ");
386 goto end;
387 }
388 else
389 {
390 chunk_t date = { optarg, 15 };
391 notAfter = asn1totime(&date, ASN1_GENERALIZEDTIME);
392 }
393 continue;
394
395 case 'o': /* --out */
396 outfile = optarg;
397 continue;
398
399 case 'd': /* --debug */
400 debug_level = atoi(optarg);
401 continue;
402
403 default:
404 usage("");
405 status = 0;
406 goto end;
407 }
408 break;
409 }
410
411 if (optind != argc)
412 {
413 usage("unexpected argument");
414 goto end;
415 }
416
417 DBG1("starting openac (strongSwan Version %s)", VERSION);
418
419 #ifdef INTEGRITY_TEST
420 DBG1("integrity test of libstrongswan code");
421 if (fips_verify_hmac_signature(hmac_key, hmac_signature))
422 {
423 DBG1(" integrity test passed");
424 }
425 else
426 {
427 DBG1(" integrity test failed");
428 exit(3);
429 }
430 #endif /* INTEGRITY_TEST */
431
432 /* load the signer's RSA private key */
433 if (keyfile != NULL)
434 {
435 signerkey = rsa_private_key_create_from_file(keyfile, &passphrase);
436
437 if (signerkey == NULL)
438 {
439 goto end;
440 }
441 }
442
443 /* load the signer's X.509 certificate */
444 if (certfile != NULL)
445 {
446 signercert = x509_create_from_file(certfile, "signer cert");
447
448 if (signercert == NULL)
449 {
450 goto end;
451 }
452 }
453
454 /* load the users's X.509 certificate */
455 if (usercertfile != NULL)
456 {
457 usercert = x509_create_from_file(usercertfile, "user cert");
458
459 if (usercert == NULL)
460 {
461 goto end;
462 }
463 }
464
465 /* compute validity interval */
466 validity = (validity)? validity : default_validity;
467 notBefore = (notBefore == UNDEFINED_TIME) ? time(NULL) : notBefore;
468 notAfter = (notAfter == UNDEFINED_TIME) ? time(NULL) + validity : notAfter;
469
470 /* build and parse attribute certificate */
471 if (usercert != NULL && signercert != NULL && signerkey != NULL)
472 {
473 /* read the serial number and increment it by one */
474 serial = read_serial();
475
476 attr_cert = build_attr_cert();
477 ac = x509ac_create_from_chunk(attr_cert);
478
479 /* write the attribute certificate to file */
480 if (chunk_write(attr_cert, outfile, "attribute cert", 0022, TRUE))
481 {
482 write_serial(serial);
483 status = 0;
484 }
485 }
486
487 end:
488 /* delete all dynamically allocated objects */
489 DESTROY_IF(signerkey);
490 DESTROY_IF(signercert);
491 DESTROY_IF(usercert);
492 DESTROY_IF(ac);
493 ietfAttr_list_destroy(groups);
494 free(serial.ptr);
495 closelog();
496 exit(status);
497 }