changed tab spacing from 8 to 4
[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 {
62 fprintf(stderr, "%s\n", mess);
63 }
64 fprintf(stderr, "Usage: openac"
65 " [--help]"
66 " [--version]"
67 " [--optionsfrom <filename>]"
68 " [--quiet]"
69 #ifdef DEBUG
70 " \\\n\t"
71 " [--debug-all]"
72 " [--debug-parsing]"
73 " [--debug-raw]"
74 " [--debug-private]"
75 #endif
76 " \\\n\t"
77 " [--days <days>]"
78 " [--hours <hours>]"
79 " \\\n\t"
80 " [--startdate <YYYYMMDDHHMMSSZ>]"
81 " [--enddate <YYYYMMDDHHMMSSZ>]"
82 " \\\n\t"
83 " --cert <certfile>"
84 " --key <keyfile>"
85 " [--password <password>]"
86 " \\\n\t"
87 " --usercert <certfile>"
88 " --groups <attr1,attr2,..>"
89 " --out <filename>"
90 "\n"
91 );
92 exit(mess == NULL? 0 : 1);
93 }
94
95 /**
96 * read the last serial number from file
97 */
98 static chunk_t 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 {
119 plog(" error reading serial number from %s: %s"
120 , OPENAC_SERIAL, ugh);
121 }
122 }
123 fclose(fd);
124 }
125 else
126 {
127 plog(" file '%s' does not exist yet - serial number set to 01"
128 , OPENAC_SERIAL);
129 }
130
131 /**
132 * conversion of read serial number to a multiprecision integer
133 * and incrementing it by one
134 * and representing it as a two's complement octet string
135 */
136 n_to_mpz(&number, bytes, len);
137 mpz_add_ui(&number, &number, 0x01);
138 serial = mpz_to_n(&number, 1 + mpz_sizeinbase(&number, 2)/BITS_PER_BYTE);
139 mpz_clear(&number);
140
141 return serial;
142 }
143
144 /**
145 * write back the last serial number to file
146 */
147 static void write_serial(chunk_t serial)
148 {
149 char buf[BUF_LEN];
150
151 FILE *fd = fopen(OPENAC_SERIAL, "w");
152
153 if (fd)
154 {
155 datatot(serial.ptr, serial.len, 16, buf, BUF_LEN);
156 plog(" serial number is %s", buf);
157 fprintf(fd, "%s\n", buf);
158 fclose(fd);
159 }
160 else
161 {
162 plog(" could not open file '%s' for writing", OPENAC_SERIAL);
163 }
164 }
165
166 /**
167 * global variables accessible by both main() and build.c
168 */
169 x509cert_t *user = NULL;
170 x509cert_t *signer = NULL;
171
172 ietfAttrList_t *groups = NULL;
173 struct RSA_private_key *signerkey = NULL;
174
175 time_t notBefore = 0;
176 time_t notAfter = 0;
177
178 chunk_t serial;
179
180 int main(int argc, char **argv)
181 {
182 char *keyfile = NULL;
183 char *certfile = NULL;
184 char *usercertfile = NULL;
185 char *outfile = NULL;
186
187 cert_t signercert = empty_cert;
188 cert_t usercert = empty_cert;
189
190 chunk_t attr_cert = empty_chunk;
191 x509acert_t *ac = NULL;
192
193 const time_t default_validity = 24*3600; /* 24 hours */
194 time_t validity = 0;
195
196 prompt_pass_t pass;
197
198 pass.secret[0] = '\0';
199 pass.prompt = TRUE;
200 pass.fd = STDIN_FILENO;
201
202 log_to_stderr = TRUE;
203
204 /* handle arguments */
205 for (;;)
206 {
207 # define DBG_OFFSET 256
208 static const struct option long_opts[] = {
209 /* name, has_arg, flag, val */
210 { "help", no_argument, NULL, 'h' },
211 { "version", no_argument, NULL, 'v' },
212 { "optionsfrom", required_argument, NULL, '+' },
213 { "quiet", no_argument, NULL, 'q' },
214 { "cert", required_argument, NULL, 'c' },
215 { "key", required_argument, NULL, 'k' },
216 { "password", required_argument, NULL, 'p' },
217 { "usercert", required_argument, NULL, 'u' },
218 { "groups", required_argument, NULL, 'g' },
219 { "days", required_argument, NULL, 'D' },
220 { "hours", required_argument, NULL, 'H' },
221 { "startdate", required_argument, NULL, 'S' },
222 { "enddate", required_argument, NULL, 'E' },
223 { "out", required_argument, NULL, 'o' },
224 #ifdef DEBUG
225 { "debug-all", no_argument, NULL, 'A' },
226 { "debug-raw", no_argument, NULL, DBG_RAW + DBG_OFFSET },
227 { "debug-parsing", no_argument, NULL, DBG_PARSING + DBG_OFFSET },
228 { "debug-private", no_argument, NULL, DBG_PRIVATE + DBG_OFFSET },
229 #endif
230 { 0,0,0,0 }
231 };
232
233 int c = getopt_long(argc, argv, "hv+:qc:k:p;u:g:D:H:S:E:o:", long_opts, NULL);
234
235 /* Note: "breaking" from case terminates loop */
236 switch (c)
237 {
238 case EOF: /* end of flags */
239 break;
240
241 case 0: /* long option already handled */
242 continue;
243
244 case ':': /* diagnostic already printed by getopt_long */
245 case '?': /* diagnostic already printed by getopt_long */
246 usage(NULL);
247 break; /* not actually reached */
248
249 case 'h': /* --help */
250 usage(NULL);
251 break; /* not actually reached */
252
253 case 'v': /* --version */
254 printf("%s\n", openac_version);
255 exit(0);
256 break; /* not actually reached */
257
258 case '+': /* --optionsfrom <filename> */
259 {
260 char path[BUF_LEN];
261
262 if (*optarg == '/') /* absolute pathname */
263 strncpy(path, optarg, BUF_LEN);
264 else /* relative pathname */
265 snprintf(path, BUF_LEN, "%s/%s", OPENAC_PATH, optarg);
266 optionsfrom(path, &argc, &argv, optind, stderr);
267 /* does not return on error */
268 }
269 continue;
270
271 case 'q': /* --quiet */
272 log_to_stderr = TRUE;
273 continue;
274
275 case 'c': /* --cert */
276 certfile = optarg;
277 continue;
278
279 case 'k': /* --key */
280 keyfile = optarg;
281 continue;
282
283 case 'p': /* --key */
284 pass.prompt = FALSE;
285 strncpy(pass.secret, optarg, sizeof(pass.secret));
286 continue;
287
288 case 'u': /* --usercert */
289 usercertfile = optarg;
290 continue;
291
292 case 'g': /* --groups */
293 decode_groups(optarg, &groups);
294 continue;
295
296 case 'D': /* --days */
297 if (optarg == NULL || !isdigit(optarg[0]))
298 usage("missing number of days");
299 {
300 char *endptr;
301 long days = strtol(optarg, &endptr, 0);
302
303 if (*endptr != '\0' || endptr == optarg || days <= 0)
304 usage("<days> must be a positive number");
305 validity += 24*3600*days;
306 }
307 continue;
308
309 case 'H': /* --hours */
310 if (optarg == NULL || !isdigit(optarg[0]))
311 usage("missing number of hours");
312 {
313 char *endptr;
314 long hours = strtol(optarg, &endptr, 0);
315
316 if (*endptr != '\0' || endptr == optarg || hours <= 0)
317 usage("<hours> must be a positive number");
318 validity += 3600*hours;
319 }
320 continue;
321
322 case 'S': /* --startdate */
323 if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z')
324 usage("date format must be YYYYMMDDHHMMSSZ");
325 {
326 chunk_t date = { optarg, 15 };
327 notBefore = asn1totime(&date, ASN1_GENERALIZEDTIME);
328 }
329 continue;
330
331 case 'E': /* --enddate */
332 if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z')
333 usage("date format must be YYYYMMDDHHMMSSZ");
334 {
335 chunk_t date = { optarg, 15 };
336 notAfter = asn1totime(&date, ASN1_GENERALIZEDTIME);
337 }
338 continue;
339
340 case 'o': /* --outt */
341 outfile = optarg;
342 continue;
343
344 #ifdef DEBUG
345 case 'A': /* --debug-all */
346 base_debugging = DBG_ALL;
347 continue;
348 #endif
349 default:
350 #ifdef DEBUG
351 if (c >= DBG_OFFSET)
352 {
353 base_debugging |= c - DBG_OFFSET;
354 continue;
355 }
356 #undef DBG_OFFSET
357 #endif
358 bad_case(c);
359 }
360 break;
361 }
362
363 init_log("openac");
364 cur_debugging = base_debugging;
365
366 if (optind != argc)
367 usage("unexpected argument");
368
369 /* load the signer's RSA private key */
370 if (keyfile != NULL)
371 {
372 err_t ugh = NULL;
373
374 signerkey = alloc_thing(RSA_private_key_t, "RSA private key");
375 ugh = load_rsa_private_key(keyfile, &pass, signerkey);
376
377 if (ugh != NULL)
378 {
379 free_RSA_private_content(signerkey);
380 pfree(signerkey);
381 plog("%s", ugh);
382 exit(1);
383 }
384 }
385
386 /* load the signer's X.509 certificate */
387 if (certfile != NULL)
388 {
389 if (!load_cert(certfile, "signer cert", &signercert))
390 exit(1);
391 signer = signercert.u.x509;
392 }
393
394 /* load the users's X.509 certificate */
395 if (usercertfile != NULL)
396 {
397 if (!load_cert(usercertfile, "user cert", &usercert))
398 exit(1);
399 user = usercert.u.x509;
400 }
401
402 /* compute validity interval */
403 validity = (validity)? validity : default_validity;
404 notBefore = (notBefore) ? notBefore : time(NULL);
405 notAfter = (notAfter) ? notAfter : notBefore + validity;
406
407 /* build and parse attribute certificate */
408 if (user != NULL && signer != NULL && signerkey != NULL)
409 {
410 /* read the serial number and increment it by one */
411 serial = read_serial();
412
413 attr_cert = build_attr_cert();
414 ac = alloc_thing(x509acert_t, "x509acert");
415 *ac = empty_ac;
416 parse_ac(attr_cert, ac);
417
418 /* write the attribute certificate to file */
419 if (write_chunk(outfile, "attribute cert", attr_cert, 0022, TRUE))
420 write_serial(serial);
421 }
422
423 /* delete all dynamic objects */
424 if (signerkey != NULL)
425 {
426 free_RSA_private_content(signerkey);
427 pfree(signerkey);
428 }
429 free_x509cert(signercert.u.x509);
430 free_x509cert(usercert.u.x509);
431 free_ietfAttrList(groups);
432 free_acert(ac);
433 pfree(serial.ptr);
434
435 #ifdef LEAK_DETECTIVE
436 report_leaks();
437 #endif /* LEAK_DETECTIVE */
438 close_log();
439 exit(0);
440 }