2ef8989969485553b21bb27bed7f207fd7690cf1
4 * @brief Generation of X.509 attribute certificates.
9 * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler
10 * Copyright (C) 2004,2007 Andreas Steffen
11 * Hochschule fuer Technik Rapperswil, Switzerland
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>.
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
38 #include <asn1/asn1.h>
40 #include <credentials/certificates/x509.h>
41 #include <credentials/certificates/ac.h>
42 #include <utils/optionsfrom.h>
45 #include <fips/fips.h>
46 #include <fips_signature.h>
47 #endif /* INTEGRITY_TEST */
49 #define OPENAC_PATH IPSEC_CONFDIR "/openac"
50 #define OPENAC_SERIAL IPSEC_CONFDIR "/openac/serial"
52 #define DEFAULT_VALIDITY 24*3600 /* seconds */
55 * @brief prints the usage of the program to the stderr
57 static void usage(const char *message
)
59 if (message
!= NULL
&& *message
!= '\0')
61 fprintf(stderr
, "%s\n", message
);
63 fprintf(stderr
, "Usage: openac"
66 " [--optionsfrom <filename>]"
69 " [--debug <level 0..4>]"
74 " [--startdate <YYYYMMDDHHMMSSZ>]"
75 " [--enddate <YYYYMMDDHHMMSSZ>]"
79 " [--password <password>]"
81 " --usercert <certfile>"
82 " --groups <attr1,attr2,..>"
90 * convert a chunk into a multi-precision integer
92 static void chunk_to_mpz(chunk_t chunk
, mpz_t number
)
94 mpz_import(number
, chunk
.len
, 1, 1, 1, 0, chunk
.ptr
);
98 * convert a multi-precision integer into a chunk
100 static chunk_t
mpz_to_chunk(mpz_t number
)
104 chunk
.len
= 1 + mpz_sizeinbase(number
, 2)/BITS_PER_BYTE
;
105 chunk
.ptr
= mpz_export(NULL
, NULL
, 1, chunk
.len
, 1, 0, number
);
110 * read the last serial number from file
112 static chunk_t
read_serial(void)
116 char buf
[BUF_LEN
], buf1
[BUF_LEN
];
117 chunk_t hex_serial
= { buf
, BUF_LEN
};
118 chunk_t last_serial
= { buf1
, BUF_LEN
};
121 FILE *fd
= fopen(OPENAC_SERIAL
, "r");
123 /* last serial number defaults to 0 */
124 *last_serial
.ptr
= 0x00;
129 if (fscanf(fd
, "%s", hex_serial
.ptr
))
131 hex_serial
.len
= strlen(hex_serial
.ptr
);
132 last_serial
= chunk_from_hex(hex_serial
, last_serial
.ptr
);
138 DBG1(" file '%s' does not exist yet - serial number set to 01", OPENAC_SERIAL
);
142 * conversion of read serial number to a multiprecision integer
143 * and incrementing it by one
144 * and representing it as a two's complement octet string
147 chunk_to_mpz(last_serial
, number
);
148 mpz_add_ui(number
, number
, 0x01);
149 serial
= mpz_to_chunk(number
);
156 * write back the last serial number to file
158 static void write_serial(chunk_t serial
)
160 FILE *fd
= fopen(OPENAC_SERIAL
, "w");
166 DBG1(" serial number is %#B", &serial
);
167 hex_serial
= chunk_to_hex(serial
, NULL
, FALSE
);
168 fprintf(fd
, "%.*s\n", hex_serial
.len
, hex_serial
.ptr
);
170 free(hex_serial
.ptr
);
174 DBG1(" could not open file '%s' for writing", OPENAC_SERIAL
);
179 * Load and parse a private key file
181 static private_key_t
* private_key_create_from_file(char *path
, chunk_t
*secret
)
184 chunk_t chunk
= chunk_empty
;
185 private_key_t
*key
= NULL
;
187 if (!pem_asn1_load_file(path
, secret
, &chunk
, &pgp
))
189 DBG1(" could not load private key file '%s'", path
);
192 key
= lib
->creds
->create(lib
->creds
, CRED_PRIVATE_KEY
, KEY_RSA
,
193 BUILD_BLOB_ASN1_DER
, chunk
, BUILD_END
);
196 DBG1(" could not parse loaded private key file '%s'", path
);
199 DBG1(" loaded private key file '%s'", path
);
204 * global variables accessible by both main() and build.c
207 static int debug_level
= 1;
208 static bool stderr_quiet
= FALSE
;
211 * openac dbg function
213 static void openac_dbg(int level
, char *fmt
, ...)
215 int priority
= LOG_INFO
;
218 if (level
<= debug_level
)
223 vfprintf(stderr
, fmt
, args
);
224 fprintf(stderr
, "\n");
226 vsyslog(priority
, fmt
, args
);
232 * @brief openac main program
234 * @param argc number of arguments
235 * @param argv pointer to the argument values
237 int main(int argc
, char **argv
)
239 certificate_t
*attr_cert
= NULL
;
240 certificate_t
*userCert
= NULL
;
241 certificate_t
*signerCert
= NULL
;
242 private_key_t
*signerKey
= NULL
;
244 time_t notBefore
= UNDEFINED_TIME
;
245 time_t notAfter
= UNDEFINED_TIME
;
248 char *keyfile
= NULL
;
249 char *certfile
= NULL
;
250 char *usercertfile
= NULL
;
251 char *outfile
= NULL
;
255 chunk_t passphrase
= { buf
, 0 };
256 chunk_t serial
= chunk_empty
;
257 chunk_t attr_chunk
= chunk_empty
;
261 /* enable openac debugging hook */
264 passphrase
.ptr
[0] = '\0';
266 openlog("openac", 0, LOG_AUTHPRIV
);
268 /* initialize library */
269 library_init(STRONGSWAN_CONF
);
270 lib
->plugins
->load(lib
->plugins
, IPSEC_PLUGINDIR
,
271 lib
->settings
->get_str(lib
->settings
, "openac.load",
272 "gmp hmac md5 random sha1 sha2 pubkey x509"));
274 /* initialize optionsfrom */
275 options_t
*options
= options_create();
277 /* handle arguments */
280 static const struct option long_opts
[] = {
281 /* name, has_arg, flag, val */
282 { "help", no_argument
, NULL
, 'h' },
283 { "version", no_argument
, NULL
, 'v' },
284 { "optionsfrom", required_argument
, NULL
, '+' },
285 { "quiet", no_argument
, NULL
, 'q' },
286 { "cert", required_argument
, NULL
, 'c' },
287 { "key", required_argument
, NULL
, 'k' },
288 { "password", required_argument
, NULL
, 'p' },
289 { "usercert", required_argument
, NULL
, 'u' },
290 { "groups", required_argument
, NULL
, 'g' },
291 { "days", required_argument
, NULL
, 'D' },
292 { "hours", required_argument
, NULL
, 'H' },
293 { "startdate", required_argument
, NULL
, 'S' },
294 { "enddate", required_argument
, NULL
, 'E' },
295 { "out", required_argument
, NULL
, 'o' },
296 { "debug", required_argument
, NULL
, 'd' },
300 int c
= getopt_long(argc
, argv
, "hv+:qc:k:p;u:g:D:H:S:E:o:d:", long_opts
, NULL
);
302 /* Note: "breaking" from case terminates loop */
305 case EOF
: /* end of flags */
308 case 0: /* long option already handled */
311 case ':': /* diagnostic already printed by getopt_long */
312 case '?': /* diagnostic already printed by getopt_long */
313 case 'h': /* --help */
318 case 'v': /* --version */
319 printf("openac (strongSwan %s)\n", VERSION
);
323 case '+': /* --optionsfrom <filename> */
327 if (*optarg
== '/') /* absolute pathname */
329 strncpy(path
, optarg
, BUF_LEN
);
331 else /* relative pathname */
333 snprintf(path
, BUF_LEN
, "%s/%s", OPENAC_PATH
, optarg
);
335 if (!options
->from(options
, path
, &argc
, &argv
, optind
))
343 case 'q': /* --quiet */
347 case 'c': /* --cert */
351 case 'k': /* --key */
355 case 'p': /* --key */
356 if (strlen(optarg
) > BUF_LEN
)
358 usage("passphrase too long");
361 strncpy(passphrase
.ptr
, optarg
, BUF_LEN
);
362 passphrase
.len
= min(strlen(optarg
), BUF_LEN
);
365 case 'u': /* --usercert */
366 usercertfile
= optarg
;
369 case 'g': /* --groups */
373 case 'D': /* --days */
374 if (optarg
== NULL
|| !isdigit(optarg
[0]))
376 usage("missing number of days");
382 long days
= strtol(optarg
, &endptr
, 0);
384 if (*endptr
!= '\0' || endptr
== optarg
|| days
<= 0)
386 usage("<days> must be a positive number");
389 validity
+= 24*3600*days
;
393 case 'H': /* --hours */
394 if (optarg
== NULL
|| !isdigit(optarg
[0]))
396 usage("missing number of hours");
402 long hours
= strtol(optarg
, &endptr
, 0);
404 if (*endptr
!= '\0' || endptr
== optarg
|| hours
<= 0)
406 usage("<hours> must be a positive number");
409 validity
+= 3600*hours
;
413 case 'S': /* --startdate */
414 if (optarg
== NULL
|| strlen(optarg
) != 15 || optarg
[14] != 'Z')
416 usage("date format must be YYYYMMDDHHMMSSZ");
421 chunk_t date
= { optarg
, 15 };
423 notBefore
= asn1_to_time(&date
, ASN1_GENERALIZEDTIME
);
427 case 'E': /* --enddate */
428 if (optarg
== NULL
|| strlen(optarg
) != 15 || optarg
[14] != 'Z')
430 usage("date format must be YYYYMMDDHHMMSSZ");
435 chunk_t date
= { optarg
, 15 };
436 notAfter
= asn1_to_time(&date
, ASN1_GENERALIZEDTIME
);
440 case 'o': /* --out */
444 case 'd': /* --debug */
445 debug_level
= atoi(optarg
);
453 /* break from loop */
459 usage("unexpected argument");
463 DBG1("starting openac (strongSwan Version %s)", VERSION
);
465 #ifdef INTEGRITY_TEST
466 DBG1("integrity test of libstrongswan code");
467 if (fips_verify_hmac_signature(hmac_key
, hmac_signature
))
469 DBG1(" integrity test passed");
473 DBG1(" integrity test failed");
477 #endif /* INTEGRITY_TEST */
479 /* load the signer's RSA private key */
482 signerKey
= private_key_create_from_file(keyfile
, &passphrase
);
484 if (signerKey
== NULL
)
490 /* load the signer's X.509 certificate */
491 if (certfile
!= NULL
)
493 signerCert
= lib
->creds
->create(lib
->creds
,
494 CRED_CERTIFICATE
, CERT_X509
,
495 BUILD_FROM_FILE
, certfile
,
498 if (signerCert
== NULL
)
504 /* load the users's X.509 certificate */
505 if (usercertfile
!= NULL
)
507 userCert
= lib
->creds
->create(lib
->creds
,
508 CRED_CERTIFICATE
, CERT_X509
,
509 BUILD_FROM_FILE
, usercertfile
,
512 if (userCert
== NULL
)
518 /* compute validity interval */
519 validity
= (validity
)? validity
: DEFAULT_VALIDITY
;
520 notBefore
= (notBefore
== UNDEFINED_TIME
) ?
time(NULL
) : notBefore
;
521 notAfter
= (notAfter
== UNDEFINED_TIME
) ?
time(NULL
) + validity
: notAfter
;
523 /* build and parse attribute certificate */
524 if (userCert
!= NULL
&& signerCert
!= NULL
&& signerKey
!= NULL
)
526 /* read the serial number and increment it by one */
527 serial
= read_serial();
529 attr_cert
= lib
->creds
->create(lib
->creds
,
530 CRED_CERTIFICATE
, CERT_X509_AC
,
531 BUILD_CERT
, userCert
->get_ref(userCert
),
532 BUILD_NOT_BEFORE_TIME
, notBefore
,
533 BUILD_NOT_AFTER_TIME
, notAfter
,
534 BUILD_SERIAL
, serial
,
535 BUILD_IETF_GROUP_ATTR
, groups
,
536 BUILD_SIGNING_CERT
, signerCert
->get_ref(signerCert
),
537 BUILD_SIGNING_KEY
, signerKey
->get_ref(signerKey
),
544 /* write the attribute certificate to file */
545 attr_chunk
= attr_cert
->get_encoding(attr_cert
);
546 if (chunk_write(attr_chunk
, outfile
, 0022, TRUE
))
548 DBG1(" wrote attribute cert file '%s' (%u bytes)", outfile
, attr_chunk
.len
);
549 write_serial(serial
);
555 usage("some of the mandatory parameters --usercert --cert --key "
560 /* delete all dynamically allocated objects */
561 DESTROY_IF(signerKey
);
562 DESTROY_IF(signerCert
);
563 DESTROY_IF(userCert
);
564 DESTROY_IF(attr_cert
);
565 free(attr_chunk
.ptr
);
569 options
->destroy(options
);