add priority management for kernel policy
[strongswan.git] / src / openac / openac.c
1 /* Generation of X.509 attribute certificates
2 * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler
3 * Copyright (C) 2004 Andreas Steffen
4 * Zuercher Hochschule Winterthur, Switzerland
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 *
16 * RCSID $Id: openac.c,v 1.18 2006/01/04 21:12:33 as Exp $
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <getopt.h>
24 #include <ctype.h>
25 #include <time.h>
26 #include <gmp.h>
27
28 #include <freeswan.h>
29
30 #include "../pluto/constants.h"
31 #include "../pluto/defs.h"
32 #include "../pluto/mp_defs.h"
33 #include "../pluto/log.h"
34 #include "../pluto/asn1.h"
35 #include "../pluto/certs.h"
36 #include "../pluto/x509.h"
37 #include "../pluto/crl.h"
38 #include "../pluto/keys.h"
39 #include "../pluto/ac.h"
40
41 #include "build.h"
42
43 #define OPENAC_PATH IPSEC_CONFDIR "/openac"
44 #define OPENAC_SERIAL IPSEC_CONFDIR "/openac/serial"
45
46 const char openac_version[] = "openac 0.3";
47
48 /* by default the CRL policy is lenient */
49 bool strict_crl_policy = FALSE;
50
51 /* by default pluto does not check crls dynamically */
52 long crl_check_interval = 0;
53
54 /* by default pluto logs out after every smartcard use */
55 bool pkcs11_keep_state = FALSE;
56
57 static void
58 usage(const char *mess)
59 {
60 if (mess != NULL && *mess != '\0')
61 fprintf(stderr, "%s\n", mess);
62 fprintf(stderr
63 , "Usage: openac"
64 " [--help]"
65 " [--version]"
66 " [--optionsfrom <filename>]"
67 " [--quiet]"
68 #ifdef DEBUG
69 " \\\n\t"
70 " [--debug-all]"
71 " [--debug-parsing]"
72 " [--debug-raw]"
73 " [--debug-private]"
74 #endif
75 " \\\n\t"
76 " [--days <days>]"
77 " [--hours <hours>]"
78 " \\\n\t"
79 " [--startdate <YYYYMMDDHHMMSSZ>]"
80 " [--enddate <YYYYMMDDHHMMSSZ>]"
81 " \\\n\t"
82 " --cert <certfile>"
83 " --key <keyfile>"
84 " [--password <password>]"
85 " \\\n\t"
86 " --usercert <certfile>"
87 " --groups <attr1,attr2,..>"
88 " --out <filename>"
89 "\n"
90 );
91 exit(mess == NULL? 0 : 1);
92 }
93
94 /*
95 * read the last serial number from file
96 */
97 static chunk_t
98 read_serial(void)
99 {
100 MP_INT number;
101
102 char buf[BUF_LEN];
103 char bytes[BUF_LEN];
104
105 FILE *fd = fopen(OPENAC_SERIAL, "r");
106
107 /* serial number defaults to 0 */
108 size_t len = 1;
109 bytes[0] = 0x00;
110
111 if (fd)
112 {
113 if (fscanf(fd, "%s", buf))
114 {
115 err_t ugh = ttodata(buf, 0, 16, bytes, BUF_LEN, &len);
116
117 if (ugh != NULL)
118 plog(" error reading serial number from %s: %s"
119 , OPENAC_SERIAL, ugh);
120 }
121 fclose(fd);
122 }
123 else
124 plog(" file '%s' does not exist yet - serial number set to 01"
125 , OPENAC_SERIAL);
126
127 /* conversion of read serial number to a multiprecision integer
128 * and incrementing it by one
129 * and representing it as a two's complement octet string
130 */
131 n_to_mpz(&number, bytes, len);
132 mpz_add_ui(&number, &number, 0x01);
133 serial = mpz_to_n(&number, 1 + mpz_sizeinbase(&number, 2)/BITS_PER_BYTE);
134 mpz_clear(&number);
135
136 return serial;
137 }
138
139 /*
140 * write back the last serial number to file
141 */
142 static void
143 write_serial(chunk_t serial)
144 {
145 char buf[BUF_LEN];
146
147 FILE *fd = fopen(OPENAC_SERIAL, "w");
148
149 if (fd)
150 {
151 datatot(serial.ptr, serial.len, 16, buf, BUF_LEN);
152 plog(" serial number is %s", buf);
153 fprintf(fd, "%s\n", buf);
154 fclose(fd);
155 }
156 else
157 plog(" could not open file '%s' for writing", OPENAC_SERIAL);
158 }
159
160 /*
161 * global variables accessible by both main() and build.c
162 */
163 x509cert_t *user = NULL;
164 x509cert_t *signer = NULL;
165
166 ietfAttrList_t *groups = NULL;
167 struct RSA_private_key *signerkey = NULL;
168
169 time_t notBefore = 0;
170 time_t notAfter = 0;
171
172 chunk_t serial;
173
174
175 int
176 main(int argc, char **argv)
177 {
178 char *keyfile = NULL;
179 char *certfile = NULL;
180 char *usercertfile = NULL;
181 char *outfile = NULL;
182
183 cert_t signercert = empty_cert;
184 cert_t usercert = empty_cert;
185
186 chunk_t attr_cert = empty_chunk;
187 x509acert_t *ac = NULL;
188
189 const time_t default_validity = 24*3600; /* 24 hours */
190 time_t validity = 0;
191
192 prompt_pass_t pass;
193
194 pass.secret[0] = '\0';
195 pass.prompt = TRUE;
196 pass.fd = STDIN_FILENO;
197
198 log_to_stderr = TRUE;
199
200 /* handle arguments */
201 for (;;)
202 {
203 # define DBG_OFFSET 256
204 static const struct option long_opts[] = {
205 /* name, has_arg, flag, val */
206 { "help", no_argument, NULL, 'h' },
207 { "version", no_argument, NULL, 'v' },
208 { "optionsfrom", required_argument, NULL, '+' },
209 { "quiet", no_argument, NULL, 'q' },
210 { "cert", required_argument, NULL, 'c' },
211 { "key", required_argument, NULL, 'k' },
212 { "password", required_argument, NULL, 'p' },
213 { "usercert", required_argument, NULL, 'u' },
214 { "groups", required_argument, NULL, 'g' },
215 { "days", required_argument, NULL, 'D' },
216 { "hours", required_argument, NULL, 'H' },
217 { "startdate", required_argument, NULL, 'S' },
218 { "enddate", required_argument, NULL, 'E' },
219 { "out", required_argument, NULL, 'o' },
220 #ifdef DEBUG
221 { "debug-all", no_argument, NULL, 'A' },
222 { "debug-raw", no_argument, NULL, DBG_RAW + DBG_OFFSET },
223 { "debug-parsing", no_argument, NULL, DBG_PARSING + DBG_OFFSET },
224 { "debug-private", no_argument, NULL, DBG_PRIVATE + DBG_OFFSET },
225 #endif
226 { 0,0,0,0 }
227 };
228
229 int c = getopt_long(argc, argv, "hv+:qc:k:p;u:g:D:H:S:E:o:", long_opts, NULL);
230
231 /* Note: "breaking" from case terminates loop */
232 switch (c)
233 {
234 case EOF: /* end of flags */
235 break;
236
237 case 0: /* long option already handled */
238 continue;
239
240 case ':': /* diagnostic already printed by getopt_long */
241 case '?': /* diagnostic already printed by getopt_long */
242 usage(NULL);
243 break; /* not actually reached */
244
245 case 'h': /* --help */
246 usage(NULL);
247 break; /* not actually reached */
248
249 case 'v': /* --version */
250 printf("%s\n", openac_version);
251 exit(0);
252 break; /* not actually reached */
253
254 case '+': /* --optionsfrom <filename> */
255 {
256 char path[BUF_LEN];
257
258 if (*optarg == '/') /* absolute pathname */
259 strncpy(path, optarg, BUF_LEN);
260 else /* relative pathname */
261 snprintf(path, BUF_LEN, "%s/%s", OPENAC_PATH, optarg);
262 optionsfrom(path, &argc, &argv, optind, stderr);
263 /* does not return on error */
264 }
265 continue;
266
267 case 'q': /* --quiet */
268 log_to_stderr = TRUE;
269 continue;
270
271 case 'c': /* --cert */
272 certfile = optarg;
273 continue;
274
275 case 'k': /* --key */
276 keyfile = optarg;
277 continue;
278
279 case 'p': /* --key */
280 pass.prompt = FALSE;
281 strncpy(pass.secret, optarg, sizeof(pass.secret));
282 continue;
283
284 case 'u': /* --usercert */
285 usercertfile = optarg;
286 continue;
287
288 case 'g': /* --groups */
289 decode_groups(optarg, &groups);
290 continue;
291
292 case 'D': /* --days */
293 if (optarg == NULL || !isdigit(optarg[0]))
294 usage("missing number of days");
295 {
296 char *endptr;
297 long days = strtol(optarg, &endptr, 0);
298
299 if (*endptr != '\0' || endptr == optarg
300 || days <= 0)
301 usage("<days> must be a positive number");
302 validity += 24*3600*days;
303 }
304 continue;
305
306 case 'H': /* --hours */
307 if (optarg == NULL || !isdigit(optarg[0]))
308 usage("missing number of hours");
309 {
310 char *endptr;
311 long hours = strtol(optarg, &endptr, 0);
312
313 if (*endptr != '\0' || endptr == optarg
314 || hours <= 0)
315 usage("<hours> must be a positive number");
316 validity += 3600*hours;
317 }
318 continue;
319
320 case 'S': /* --startdate */
321 if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z')
322 usage("date format must be YYYYMMDDHHMMSSZ");
323 {
324 chunk_t date = { optarg, 15 };
325 notBefore = asn1totime(&date, ASN1_GENERALIZEDTIME);
326 }
327 continue;
328
329 case 'E': /* --enddate */
330 if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z')
331 usage("date format must be YYYYMMDDHHMMSSZ");
332 {
333 chunk_t date = { optarg, 15 };
334 notAfter = asn1totime(&date, ASN1_GENERALIZEDTIME);
335 }
336 continue;
337
338 case 'o': /* --outt */
339 outfile = optarg;
340 continue ;
341
342 #ifdef DEBUG
343 case 'A': /* --debug-all */
344 base_debugging = DBG_ALL;
345 continue;
346 #endif
347 default:
348 #ifdef DEBUG
349 if (c >= DBG_OFFSET)
350 {
351 base_debugging |= c - DBG_OFFSET;
352 continue;
353 }
354 #undef DBG_OFFSET
355 #endif
356 bad_case(c);
357 }
358 break;
359 }
360
361 init_log("openac");
362 cur_debugging = base_debugging;
363
364 if (optind != argc)
365 usage("unexpected argument");
366
367 /* load the signer's RSA private key */
368 if (keyfile != NULL)
369 {
370 err_t ugh = NULL;
371
372 signerkey = alloc_thing(RSA_private_key_t, "RSA private key");
373 ugh = load_rsa_private_key(keyfile, &pass, signerkey);
374
375 if (ugh != NULL)
376 {
377 free_RSA_private_content(signerkey);
378 pfree(signerkey);
379 plog("%s", ugh);
380 exit(1);
381 }
382 }
383
384 /* load the signer's X.509 certificate */
385 if (certfile != NULL)
386 {
387 if (!load_cert(certfile, "signer cert", &signercert))
388 exit(1);
389 signer = signercert.u.x509;
390 }
391
392 /* load the users's X.509 certificate */
393 if (usercertfile != NULL)
394 {
395 if (!load_cert(usercertfile, "user cert", &usercert))
396 exit(1);
397 user = usercert.u.x509;
398 }
399
400 /* compute validity interval */
401 validity = (validity)? validity : default_validity;
402 notBefore = (notBefore) ? notBefore : time(NULL);
403 notAfter = (notAfter) ? notAfter : notBefore + validity;
404
405 /* build and parse attribute certificate */
406 if (user != NULL && signer != NULL && signerkey != NULL)
407 {
408 /* read the serial number and increment it by one */
409 serial = read_serial();
410
411 attr_cert = build_attr_cert();
412 ac = alloc_thing(x509acert_t, "x509acert");
413 *ac = empty_ac;
414 parse_ac(attr_cert, ac);
415
416 /* write the attribute certificate to file */
417 if (write_chunk(outfile, "attribute cert", attr_cert, 0022, TRUE))
418 write_serial(serial);
419 }
420
421 /* delete all dynamic objects */
422 if (signerkey != NULL)
423 {
424 free_RSA_private_content(signerkey);
425 pfree(signerkey);
426 }
427 free_x509cert(signercert.u.x509);
428 free_x509cert(usercert.u.x509);
429 free_ietfAttrList(groups);
430 free_acert(ac);
431 pfree(serial.ptr);
432
433 #ifdef LEAK_DETECTIVE
434 report_leaks();
435 #endif /* LEAK_DETECTIVE */
436 close_log();
437 exit(0);
438 }