Show contents of the CP payload in message_t stringification
[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
33 #include <library.h>
34 #include <debug.h>
35 #include <asn1/asn1.h>
36 #include <credentials/certificates/x509.h>
37 #include <credentials/certificates/ac.h>
38 #include <credentials/keys/private_key.h>
39 #include <utils/optionsfrom.h>
40
41 #define OPENAC_PATH IPSEC_CONFDIR "/openac"
42 #define OPENAC_SERIAL IPSEC_CONFDIR "/openac/serial"
43
44 #define DEFAULT_VALIDITY 24*3600 /* seconds */
45
46 /**
47 * @brief prints the usage of the program to the stderr
48 */
49 static void usage(const char *message)
50 {
51 if (message != NULL && *message != '\0')
52 {
53 fprintf(stderr, "%s\n", message);
54 }
55 fprintf(stderr, "Usage: openac"
56 " [--help]"
57 " [--version]"
58 " [--optionsfrom <filename>]"
59 " [--quiet]"
60 " \\\n\t"
61 " [--debug <level 0..4>]"
62 " \\\n\t"
63 " [--days <days>]"
64 " [--hours <hours>]"
65 " \\\n\t"
66 " [--startdate <YYYYMMDDHHMMSSZ>]"
67 " [--enddate <YYYYMMDDHHMMSSZ>]"
68 " \\\n\t"
69 " --cert <certfile>"
70 " --key <keyfile>"
71 " [--password <password>]"
72 " \\\n\t"
73 " --usercert <certfile>"
74 " --groups <attr1,attr2,..>"
75 " --out <filename>"
76 "\n"
77 );
78 }
79
80 /**
81 * read the last serial number from file
82 */
83 static chunk_t read_serial(void)
84 {
85 chunk_t hex, serial = chunk_empty;
86 char one[] = {0x01};
87 FILE *fd;
88
89 fd = fopen(OPENAC_SERIAL, "r");
90 if (fd)
91 {
92 hex = chunk_alloca(64);
93 hex.len = fread(hex.ptr, 1, hex.len, fd);
94 if (hex.len)
95 {
96 /* remove any terminating newline character */
97 if (hex.ptr[hex.len-1] == '\n')
98 {
99 hex.len--;
100 }
101 serial = chunk_alloca((hex.len / 2) + (hex.len % 2));
102 serial = chunk_from_hex(hex, serial.ptr);
103 }
104 fclose(fd);
105 }
106 else
107 {
108 DBG1(DBG_LIB, " file '%s' does not exist yet - serial number "
109 "set to 01", OPENAC_SERIAL);
110 }
111 if (!serial.len)
112 {
113 return chunk_clone(chunk_create(one, 1));
114 }
115 if (chunk_increment(serial))
116 { /* overflow, prepend 0x01 */
117 return chunk_cat("cc", chunk_create(one, 1), serial);
118 }
119 return chunk_clone(serial);
120 }
121
122 /**
123 * write back the last serial number to file
124 */
125 static void write_serial(chunk_t serial)
126 {
127 FILE *fd = fopen(OPENAC_SERIAL, "w");
128
129 if (fd)
130 {
131 chunk_t hex_serial;
132
133 DBG1(DBG_LIB, " serial number is %#B", &serial);
134 hex_serial = chunk_to_hex(serial, NULL, FALSE);
135 fprintf(fd, "%.*s\n", hex_serial.len, hex_serial.ptr);
136 fclose(fd);
137 free(hex_serial.ptr);
138 }
139 else
140 {
141 DBG1(DBG_LIB, " could not open file '%s' for writing", OPENAC_SERIAL);
142 }
143 }
144
145 /**
146 * global variables accessible by both main() and build.c
147 */
148
149 static int debug_level = 1;
150 static bool stderr_quiet = FALSE;
151
152 /**
153 * openac dbg function
154 */
155 static void openac_dbg(debug_t group, level_t level, char *fmt, ...)
156 {
157 int priority = LOG_INFO;
158 char buffer[8192];
159 char *current = buffer, *next;
160 va_list args;
161
162 if (level <= debug_level)
163 {
164 if (!stderr_quiet)
165 {
166 va_start(args, fmt);
167 vfprintf(stderr, fmt, args);
168 fprintf(stderr, "\n");
169 va_end(args);
170 }
171
172 /* write in memory buffer first */
173 va_start(args, fmt);
174 vsnprintf(buffer, sizeof(buffer), fmt, args);
175 va_end(args);
176
177 /* do a syslog with every line */
178 while (current)
179 {
180 next = strchr(current, '\n');
181 if (next)
182 {
183 *(next++) = '\0';
184 }
185 syslog(priority, "%s\n", current);
186 current = next;
187 }
188 }
189 }
190
191 /**
192 * @brief openac main program
193 *
194 * @param argc number of arguments
195 * @param argv pointer to the argument values
196 */
197 int main(int argc, char **argv)
198 {
199 certificate_t *attr_cert = NULL;
200 certificate_t *userCert = NULL;
201 certificate_t *signerCert = NULL;
202 private_key_t *signerKey = NULL;
203
204 time_t notBefore = UNDEFINED_TIME;
205 time_t notAfter = UNDEFINED_TIME;
206 time_t validity = 0;
207
208 char *keyfile = NULL;
209 char *certfile = NULL;
210 char *usercertfile = NULL;
211 char *outfile = NULL;
212 char *groups = "";
213 char buf[BUF_LEN];
214
215 chunk_t passphrase = { buf, 0 };
216 chunk_t serial = chunk_empty;
217 chunk_t attr_chunk = chunk_empty;
218
219 int status = 1;
220
221 /* enable openac debugging hook */
222 dbg = openac_dbg;
223
224 passphrase.ptr[0] = '\0';
225
226 openlog("openac", 0, LOG_AUTHPRIV);
227
228 /* initialize library */
229 atexit(library_deinit);
230 if (!library_init(NULL))
231 {
232 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
233 }
234 if (lib->integrity &&
235 !lib->integrity->check_file(lib->integrity, "openac", argv[0]))
236 {
237 fprintf(stderr, "integrity check of openac failed\n");
238 exit(SS_RC_DAEMON_INTEGRITY);
239 }
240 if (!lib->plugins->load(lib->plugins, NULL,
241 lib->settings->get_str(lib->settings, "openac.load", PLUGINS)))
242 {
243 exit(SS_RC_INITIALIZATION_FAILED);
244 }
245
246 /* initialize optionsfrom */
247 options_t *options = options_create();
248
249 /* handle arguments */
250 for (;;)
251 {
252 static const struct option long_opts[] = {
253 /* name, has_arg, flag, val */
254 { "help", no_argument, NULL, 'h' },
255 { "version", no_argument, NULL, 'v' },
256 { "optionsfrom", required_argument, NULL, '+' },
257 { "quiet", no_argument, NULL, 'q' },
258 { "cert", required_argument, NULL, 'c' },
259 { "key", required_argument, NULL, 'k' },
260 { "password", required_argument, NULL, 'p' },
261 { "usercert", required_argument, NULL, 'u' },
262 { "groups", required_argument, NULL, 'g' },
263 { "days", required_argument, NULL, 'D' },
264 { "hours", required_argument, NULL, 'H' },
265 { "startdate", required_argument, NULL, 'S' },
266 { "enddate", required_argument, NULL, 'E' },
267 { "out", required_argument, NULL, 'o' },
268 { "debug", required_argument, NULL, 'd' },
269 { 0,0,0,0 }
270 };
271
272 int c = getopt_long(argc, argv, "hv+:qc:k:p;u:g:D:H:S:E:o:d:", long_opts, NULL);
273
274 /* Note: "breaking" from case terminates loop */
275 switch (c)
276 {
277 case EOF: /* end of flags */
278 break;
279
280 case 0: /* long option already handled */
281 continue;
282
283 case ':': /* diagnostic already printed by getopt_long */
284 case '?': /* diagnostic already printed by getopt_long */
285 case 'h': /* --help */
286 usage(NULL);
287 status = 1;
288 goto end;
289
290 case 'v': /* --version */
291 printf("openac (strongSwan %s)\n", VERSION);
292 status = 0;
293 goto end;
294
295 case '+': /* --optionsfrom <filename> */
296 {
297 char path[BUF_LEN];
298
299 if (*optarg == '/') /* absolute pathname */
300 {
301 strncpy(path, optarg, BUF_LEN);
302 }
303 else /* relative pathname */
304 {
305 snprintf(path, BUF_LEN, "%s/%s", OPENAC_PATH, optarg);
306 }
307 if (!options->from(options, path, &argc, &argv, optind))
308 {
309 status = 1;
310 goto end;
311 }
312 }
313 continue;
314
315 case 'q': /* --quiet */
316 stderr_quiet = TRUE;
317 continue;
318
319 case 'c': /* --cert */
320 certfile = optarg;
321 continue;
322
323 case 'k': /* --key */
324 keyfile = optarg;
325 continue;
326
327 case 'p': /* --key */
328 if (strlen(optarg) > BUF_LEN)
329 {
330 usage("passphrase too long");
331 goto end;
332 }
333 strncpy(passphrase.ptr, optarg, BUF_LEN);
334 passphrase.len = min(strlen(optarg), BUF_LEN);
335 continue;
336
337 case 'u': /* --usercert */
338 usercertfile = optarg;
339 continue;
340
341 case 'g': /* --groups */
342 groups = optarg;
343 continue;
344
345 case 'D': /* --days */
346 if (optarg == NULL || !isdigit(optarg[0]))
347 {
348 usage("missing number of days");
349 goto end;
350 }
351 else
352 {
353 char *endptr;
354 long days = strtol(optarg, &endptr, 0);
355
356 if (*endptr != '\0' || endptr == optarg || days <= 0)
357 {
358 usage("<days> must be a positive number");
359 goto end;
360 }
361 validity += 24*3600*days;
362 }
363 continue;
364
365 case 'H': /* --hours */
366 if (optarg == NULL || !isdigit(optarg[0]))
367 {
368 usage("missing number of hours");
369 goto end;
370 }
371 else
372 {
373 char *endptr;
374 long hours = strtol(optarg, &endptr, 0);
375
376 if (*endptr != '\0' || endptr == optarg || hours <= 0)
377 {
378 usage("<hours> must be a positive number");
379 goto end;
380 }
381 validity += 3600*hours;
382 }
383 continue;
384
385 case 'S': /* --startdate */
386 if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z')
387 {
388 usage("date format must be YYYYMMDDHHMMSSZ");
389 goto end;
390 }
391 else
392 {
393 chunk_t date = { optarg, 15 };
394
395 notBefore = asn1_to_time(&date, ASN1_GENERALIZEDTIME);
396 }
397 continue;
398
399 case 'E': /* --enddate */
400 if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z')
401 {
402 usage("date format must be YYYYMMDDHHMMSSZ");
403 goto end;
404 }
405 else
406 {
407 chunk_t date = { optarg, 15 };
408 notAfter = asn1_to_time(&date, ASN1_GENERALIZEDTIME);
409 }
410 continue;
411
412 case 'o': /* --out */
413 outfile = optarg;
414 continue;
415
416 case 'd': /* --debug */
417 debug_level = atoi(optarg);
418 continue;
419
420 default:
421 usage("");
422 status = 0;
423 goto end;
424 }
425 /* break from loop */
426 break;
427 }
428
429 if (optind != argc)
430 {
431 usage("unexpected argument");
432 goto end;
433 }
434
435 DBG1(DBG_LIB, "starting openac (strongSwan Version %s)", VERSION);
436
437 /* load the signer's RSA private key */
438 if (keyfile != NULL)
439 {
440 signerKey = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA,
441 BUILD_FROM_FILE, keyfile,
442 BUILD_PASSPHRASE, passphrase,
443 BUILD_END);
444 if (signerKey == NULL)
445 {
446 goto end;
447 }
448 DBG1(DBG_LIB, " loaded private key file '%s'", keyfile);
449 }
450
451 /* load the signer's X.509 certificate */
452 if (certfile != NULL)
453 {
454 signerCert = lib->creds->create(lib->creds,
455 CRED_CERTIFICATE, CERT_X509,
456 BUILD_FROM_FILE, certfile,
457 BUILD_END);
458 if (signerCert == NULL)
459 {
460 goto end;
461 }
462 }
463
464 /* load the users's X.509 certificate */
465 if (usercertfile != NULL)
466 {
467 userCert = lib->creds->create(lib->creds,
468 CRED_CERTIFICATE, CERT_X509,
469 BUILD_FROM_FILE, usercertfile,
470 BUILD_END);
471 if (userCert == NULL)
472 {
473 goto end;
474 }
475 }
476
477 /* compute validity interval */
478 validity = (validity)? validity : DEFAULT_VALIDITY;
479 notBefore = (notBefore == UNDEFINED_TIME) ? time(NULL) : notBefore;
480 notAfter = (notAfter == UNDEFINED_TIME) ? time(NULL) + validity : notAfter;
481
482 /* build and parse attribute certificate */
483 if (userCert != NULL && signerCert != NULL && signerKey != NULL)
484 {
485 /* read the serial number and increment it by one */
486 serial = read_serial();
487
488 attr_cert = lib->creds->create(lib->creds,
489 CRED_CERTIFICATE, CERT_X509_AC,
490 BUILD_CERT, userCert,
491 BUILD_NOT_BEFORE_TIME, notBefore,
492 BUILD_NOT_AFTER_TIME, notAfter,
493 BUILD_SERIAL, serial,
494 BUILD_IETF_GROUP_ATTR, groups,
495 BUILD_SIGNING_CERT, signerCert,
496 BUILD_SIGNING_KEY, signerKey,
497 BUILD_END);
498 if (!attr_cert)
499 {
500 goto end;
501 }
502
503 /* write the attribute certificate to file */
504 attr_chunk = attr_cert->get_encoding(attr_cert);
505 if (chunk_write(attr_chunk, outfile, "attribute cert", 0022, TRUE))
506 {
507 write_serial(serial);
508 status = 0;
509 }
510 }
511 else
512 {
513 usage("some of the mandatory parameters --usercert --cert --key "
514 "are missing");
515 }
516
517 end:
518 /* delete all dynamically allocated objects */
519 DESTROY_IF(signerKey);
520 DESTROY_IF(signerCert);
521 DESTROY_IF(userCert);
522 DESTROY_IF(attr_cert);
523 free(attr_chunk.ptr);
524 free(serial.ptr);
525 closelog();
526 dbg = dbg_default;
527 options->destroy(options);
528 exit(status);
529 }