/* Generation of X.509 attribute certificates * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler * Copyright (C) 2004 Andreas Steffen * Zuercher Hochschule Winterthur, Switzerland * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * RCSID $Id: openac.c,v 1.18 2006/01/04 21:12:33 as Exp $ */ #include #include #include #include #include #include #include #include #include #include "../pluto/constants.h" #include "../pluto/defs.h" #include "../pluto/mp_defs.h" #include "../pluto/log.h" #include "../pluto/asn1.h" #include "../pluto/certs.h" #include "../pluto/x509.h" #include "../pluto/crl.h" #include "../pluto/keys.h" #include "../pluto/ac.h" #include "build.h" #define OPENAC_PATH IPSEC_CONFDIR "/openac" #define OPENAC_SERIAL IPSEC_CONFDIR "/openac/serial" const char openac_version[] = "openac 0.3"; /* by default the CRL policy is lenient */ bool strict_crl_policy = FALSE; /* by default pluto does not check crls dynamically */ long crl_check_interval = 0; /* by default pluto logs out after every smartcard use */ bool pkcs11_keep_state = FALSE; static void usage(const char *mess) { if (mess != NULL && *mess != '\0') { fprintf(stderr, "%s\n", mess); } fprintf(stderr, "Usage: openac" " [--help]" " [--version]" " [--optionsfrom ]" " [--quiet]" #ifdef DEBUG " \\\n\t" " [--debug-all]" " [--debug-parsing]" " [--debug-raw]" " [--debug-private]" #endif " \\\n\t" " [--days ]" " [--hours ]" " \\\n\t" " [--startdate ]" " [--enddate ]" " \\\n\t" " --cert " " --key " " [--password ]" " \\\n\t" " --usercert " " --groups " " --out " "\n" ); exit(mess == NULL? 0 : 1); } /** * read the last serial number from file */ static chunk_t read_serial(void) { MP_INT number; char buf[BUF_LEN]; char bytes[BUF_LEN]; FILE *fd = fopen(OPENAC_SERIAL, "r"); /* serial number defaults to 0 */ size_t len = 1; bytes[0] = 0x00; if (fd) { if (fscanf(fd, "%s", buf)) { err_t ugh = ttodata(buf, 0, 16, bytes, BUF_LEN, &len); if (ugh != NULL) { plog(" error reading serial number from %s: %s" , OPENAC_SERIAL, ugh); } } fclose(fd); } else { plog(" file '%s' does not exist yet - serial number set to 01" , OPENAC_SERIAL); } /** * conversion of read serial number to a multiprecision integer * and incrementing it by one * and representing it as a two's complement octet string */ n_to_mpz(&number, bytes, len); mpz_add_ui(&number, &number, 0x01); serial = mpz_to_n(&number, 1 + mpz_sizeinbase(&number, 2)/BITS_PER_BYTE); mpz_clear(&number); return serial; } /** * write back the last serial number to file */ static void write_serial(chunk_t serial) { char buf[BUF_LEN]; FILE *fd = fopen(OPENAC_SERIAL, "w"); if (fd) { datatot(serial.ptr, serial.len, 16, buf, BUF_LEN); plog(" serial number is %s", buf); fprintf(fd, "%s\n", buf); fclose(fd); } else { plog(" could not open file '%s' for writing", OPENAC_SERIAL); } } /** * global variables accessible by both main() and build.c */ x509cert_t *user = NULL; x509cert_t *signer = NULL; ietfAttrList_t *groups = NULL; struct RSA_private_key *signerkey = NULL; time_t notBefore = 0; time_t notAfter = 0; chunk_t serial; int main(int argc, char **argv) { char *keyfile = NULL; char *certfile = NULL; char *usercertfile = NULL; char *outfile = NULL; cert_t signercert = empty_cert; cert_t usercert = empty_cert; chunk_t attr_cert = empty_chunk; x509acert_t *ac = NULL; const time_t default_validity = 24*3600; /* 24 hours */ time_t validity = 0; prompt_pass_t pass; pass.secret[0] = '\0'; pass.prompt = TRUE; pass.fd = STDIN_FILENO; log_to_stderr = TRUE; /* handle arguments */ for (;;) { # define DBG_OFFSET 256 static const struct option long_opts[] = { /* name, has_arg, flag, val */ { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { "optionsfrom", required_argument, NULL, '+' }, { "quiet", no_argument, NULL, 'q' }, { "cert", required_argument, NULL, 'c' }, { "key", required_argument, NULL, 'k' }, { "password", required_argument, NULL, 'p' }, { "usercert", required_argument, NULL, 'u' }, { "groups", required_argument, NULL, 'g' }, { "days", required_argument, NULL, 'D' }, { "hours", required_argument, NULL, 'H' }, { "startdate", required_argument, NULL, 'S' }, { "enddate", required_argument, NULL, 'E' }, { "out", required_argument, NULL, 'o' }, #ifdef DEBUG { "debug-all", no_argument, NULL, 'A' }, { "debug-raw", no_argument, NULL, DBG_RAW + DBG_OFFSET }, { "debug-parsing", no_argument, NULL, DBG_PARSING + DBG_OFFSET }, { "debug-private", no_argument, NULL, DBG_PRIVATE + DBG_OFFSET }, #endif { 0,0,0,0 } }; int c = getopt_long(argc, argv, "hv+:qc:k:p;u:g:D:H:S:E:o:", long_opts, NULL); /* Note: "breaking" from case terminates loop */ switch (c) { case EOF: /* end of flags */ break; case 0: /* long option already handled */ continue; case ':': /* diagnostic already printed by getopt_long */ case '?': /* diagnostic already printed by getopt_long */ usage(NULL); break; /* not actually reached */ case 'h': /* --help */ usage(NULL); break; /* not actually reached */ case 'v': /* --version */ printf("%s\n", openac_version); exit(0); break; /* not actually reached */ case '+': /* --optionsfrom */ { char path[BUF_LEN]; if (*optarg == '/') /* absolute pathname */ strncpy(path, optarg, BUF_LEN); else /* relative pathname */ snprintf(path, BUF_LEN, "%s/%s", OPENAC_PATH, optarg); optionsfrom(path, &argc, &argv, optind, stderr); /* does not return on error */ } continue; case 'q': /* --quiet */ log_to_stderr = TRUE; continue; case 'c': /* --cert */ certfile = optarg; continue; case 'k': /* --key */ keyfile = optarg; continue; case 'p': /* --key */ pass.prompt = FALSE; strncpy(pass.secret, optarg, sizeof(pass.secret)); continue; case 'u': /* --usercert */ usercertfile = optarg; continue; case 'g': /* --groups */ decode_groups(optarg, &groups); continue; case 'D': /* --days */ if (optarg == NULL || !isdigit(optarg[0])) usage("missing number of days"); { char *endptr; long days = strtol(optarg, &endptr, 0); if (*endptr != '\0' || endptr == optarg || days <= 0) usage(" must be a positive number"); validity += 24*3600*days; } continue; case 'H': /* --hours */ if (optarg == NULL || !isdigit(optarg[0])) usage("missing number of hours"); { char *endptr; long hours = strtol(optarg, &endptr, 0); if (*endptr != '\0' || endptr == optarg || hours <= 0) usage(" must be a positive number"); validity += 3600*hours; } continue; case 'S': /* --startdate */ if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z') usage("date format must be YYYYMMDDHHMMSSZ"); { chunk_t date = { optarg, 15 }; notBefore = asn1totime(&date, ASN1_GENERALIZEDTIME); } continue; case 'E': /* --enddate */ if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z') usage("date format must be YYYYMMDDHHMMSSZ"); { chunk_t date = { optarg, 15 }; notAfter = asn1totime(&date, ASN1_GENERALIZEDTIME); } continue; case 'o': /* --outt */ outfile = optarg; continue; #ifdef DEBUG case 'A': /* --debug-all */ base_debugging = DBG_ALL; continue; #endif default: #ifdef DEBUG if (c >= DBG_OFFSET) { base_debugging |= c - DBG_OFFSET; continue; } #undef DBG_OFFSET #endif bad_case(c); } break; } init_log("openac"); cur_debugging = base_debugging; if (optind != argc) usage("unexpected argument"); /* load the signer's RSA private key */ if (keyfile != NULL) { err_t ugh = NULL; signerkey = alloc_thing(RSA_private_key_t, "RSA private key"); ugh = load_rsa_private_key(keyfile, &pass, signerkey); if (ugh != NULL) { free_RSA_private_content(signerkey); pfree(signerkey); plog("%s", ugh); exit(1); } } /* load the signer's X.509 certificate */ if (certfile != NULL) { if (!load_cert(certfile, "signer cert", &signercert)) exit(1); signer = signercert.u.x509; } /* load the users's X.509 certificate */ if (usercertfile != NULL) { if (!load_cert(usercertfile, "user cert", &usercert)) exit(1); user = usercert.u.x509; } /* compute validity interval */ validity = (validity)? validity : default_validity; notBefore = (notBefore) ? notBefore : time(NULL); notAfter = (notAfter) ? notAfter : notBefore + validity; /* build and parse attribute certificate */ if (user != NULL && signer != NULL && signerkey != NULL) { /* read the serial number and increment it by one */ serial = read_serial(); attr_cert = build_attr_cert(); ac = alloc_thing(x509acert_t, "x509acert"); *ac = empty_ac; parse_ac(attr_cert, ac); /* write the attribute certificate to file */ if (write_chunk(outfile, "attribute cert", attr_cert, 0022, TRUE)) write_serial(serial); } /* delete all dynamic objects */ if (signerkey != NULL) { free_RSA_private_content(signerkey); pfree(signerkey); } free_x509cert(signercert.u.x509); free_x509cert(usercert.u.x509); free_ietfAttrList(groups); free_acert(ac); pfree(serial.ptr); #ifdef LEAK_DETECTIVE report_leaks(); #endif /* LEAK_DETECTIVE */ close_log(); exit(0); }