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
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>.
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
16 * RCSID $Id: openac.c,v 1.18 2006/01/04 21:12:33 as Exp $
29 #include <asn1/asn1.h>
30 #include <asn1/ttodata.h>
31 #include <crypto/ac.h>
32 #include <crypto/ietf_attr_list.h>
33 #include <utils/optionsfrom.h>
37 #define OPENAC_PATH IPSEC_CONFDIR "/openac"
38 #define OPENAC_SERIAL IPSEC_CONFDIR "/openac/serial"
40 const char openac_version
[] = "openac 0.4";
42 static void usage(const char *mess
)
44 if (mess
!= NULL
&& *mess
!= '\0')
46 fprintf(stderr
, "%s\n", mess
);
48 fprintf(stderr
, "Usage: openac"
51 " [--optionsfrom <filename>]"
64 " [--startdate <YYYYMMDDHHMMSSZ>]"
65 " [--enddate <YYYYMMDDHHMMSSZ>]"
69 " [--password <password>]"
71 " --usercert <certfile>"
72 " --groups <attr1,attr2,..>"
76 exit(mess
== NULL?
0 : 1);
80 * convert a chunk into a multi-precision integer
82 static void chunk_to_mpz(chunk_t chunk
, mpz_t number
)
84 mpz_import(number
, chunk
.len
, 1, 1, 1, 0, chunk
.ptr
);
88 * convert a multi-precision integer into a chunk
90 static chunk_t
mpz_to_chunk(mpz_t number
)
94 chunk
.len
= 1 + mpz_sizeinbase(number
, 2)/BITS_PER_BYTE
;
95 chunk
.ptr
= mpz_export(NULL
, NULL
, 1, chunk
.len
, 1, 0, number
);
100 * read the last serial number from file
102 static chunk_t
read_serial(void)
106 char buf
[BUF_LEN
], buf1
[BUF_LEN
];
107 chunk_t last_serial
= { buf1
, BUF_LEN
};
110 FILE *fd
= fopen(OPENAC_SERIAL
, "r");
112 /* last serial number defaults to 0 */
113 *last_serial
.ptr
= 0x00;
118 if (fscanf(fd
, "%s", buf
))
120 err_t ugh
= ttodata(buf
, 0, 16, last_serial
.ptr
, BUF_LEN
, &last_serial
.len
);
124 DBG1(" error reading serial number from %s: %s",
132 DBG1(" file '%s' does not exist yet - serial number set to 01", OPENAC_SERIAL
);
136 * conversion of read serial number to a multiprecision integer
137 * and incrementing it by one
138 * and representing it as a two's complement octet string
140 printf("last_serial: '%#B'\n", &last_serial
);
142 chunk_to_mpz(last_serial
, number
);
143 mpz_add_ui(number
, number
, 0x01);
144 serial
= mpz_to_chunk(number
);
145 printf("serial: '%#B'\n", &serial
);
152 * write back the last serial number to file
154 static void write_serial(chunk_t serial
)
156 FILE *fd
= fopen(OPENAC_SERIAL
, "w");
160 DBG1(" serial number is %#B", &serial
);
161 fprintf(fd
, "%#B\n", &serial
);
166 DBG1(" could not open file '%s' for writing", OPENAC_SERIAL
);
171 * global variables accessible by both main() and build.c
173 x509_t
*usercert
= NULL
;
174 x509_t
*signercert
= NULL
;
176 linked_list_t
*groups
= NULL
;
177 rsa_private_key_t
*signerkey
= NULL
;
179 time_t notBefore
= UNDEFINED_TIME
;
180 time_t notAfter
= UNDEFINED_TIME
;
184 int main(int argc
, char **argv
)
186 char *keyfile
= NULL
;
187 char *certfile
= NULL
;
188 char *usercertfile
= NULL
;
189 char *outfile
= NULL
;
192 chunk_t passphrase
= { buf
, 0 };
193 chunk_t attr_cert
= chunk_empty
;
196 const time_t default_validity
= 24*3600; /* 24 hours */
199 passphrase
.ptr
[0] = '\0';
201 groups
= linked_list_create();
203 /* handle arguments */
206 # define DBG_OFFSET 256
207 static const struct option long_opts
[] = {
208 /* name, has_arg, flag, val */
209 { "help", no_argument
, NULL
, 'h' },
210 { "version", no_argument
, NULL
, 'v' },
211 { "optionsfrom", required_argument
, NULL
, '+' },
212 { "quiet", no_argument
, NULL
, 'q' },
213 { "cert", required_argument
, NULL
, 'c' },
214 { "key", required_argument
, NULL
, 'k' },
215 { "password", required_argument
, NULL
, 'p' },
216 { "usercert", required_argument
, NULL
, 'u' },
217 { "groups", required_argument
, NULL
, 'g' },
218 { "days", required_argument
, NULL
, 'D' },
219 { "hours", required_argument
, NULL
, 'H' },
220 { "startdate", required_argument
, NULL
, 'S' },
221 { "enddate", required_argument
, NULL
, 'E' },
222 { "out", required_argument
, NULL
, 'o' },
224 { "debug-all", no_argument
, NULL
, 'A' },
225 { "debug-raw", no_argument
, NULL
, DBG_RAW
+ DBG_OFFSET
},
226 { "debug-parsing", no_argument
, NULL
, DBG_PARSING
+ DBG_OFFSET
},
227 { "debug-private", no_argument
, NULL
, DBG_PRIVATE
+ DBG_OFFSET
},
232 int c
= getopt_long(argc
, argv
, "hv+:qc:k:p;u:g:D:H:S:E:o:", long_opts
, NULL
);
234 /* Note: "breaking" from case terminates loop */
237 case EOF
: /* end of flags */
240 case 0: /* long option already handled */
243 case ':': /* diagnostic already printed by getopt_long */
244 case '?': /* diagnostic already printed by getopt_long */
246 break; /* not actually reached */
248 case 'h': /* --help */
250 break; /* not actually reached */
252 case 'v': /* --version */
253 printf("%s\n", openac_version
);
255 break; /* not actually reached */
257 case '+': /* --optionsfrom <filename> */
261 if (*optarg
== '/') /* absolute pathname */
263 strncpy(path
, optarg
, BUF_LEN
);
265 else /* relative pathname */
267 snprintf(path
, BUF_LEN
, "%s/%s", OPENAC_PATH
, optarg
);
269 optionsfrom(path
, &argc
, &argv
, optind
, stderr
);
270 /* does not return on error */
274 case 'q': /* --quiet */
275 /* TODO log to syslog only */
278 case 'c': /* --cert */
282 case 'k': /* --key */
286 case 'p': /* --key */
287 if (strlen(optarg
) > BUF_LEN
)
289 usage("passphrase too long");
291 strncpy(passphrase
.ptr
, optarg
, BUF_LEN
);
292 passphrase
.len
= min(strlen(optarg
), BUF_LEN
);
295 case 'u': /* --usercert */
296 usercertfile
= optarg
;
299 case 'g': /* --groups */
300 ietfAttr_list_create_from_string(optarg
, groups
);
303 case 'D': /* --days */
304 if (optarg
== NULL
|| !isdigit(optarg
[0]))
306 usage("missing number of days");
311 long days
= strtol(optarg
, &endptr
, 0);
313 if (*endptr
!= '\0' || endptr
== optarg
|| days
<= 0)
315 usage("<days> must be a positive number");
317 validity
+= 24*3600*days
;
321 case 'H': /* --hours */
322 if (optarg
== NULL
|| !isdigit(optarg
[0]))
324 usage("missing number of hours");
329 long hours
= strtol(optarg
, &endptr
, 0);
331 if (*endptr
!= '\0' || endptr
== optarg
|| hours
<= 0)
333 usage("<hours> must be a positive number");
335 validity
+= 3600*hours
;
339 case 'S': /* --startdate */
340 if (optarg
== NULL
|| strlen(optarg
) != 15 || optarg
[14] != 'Z')
342 usage("date format must be YYYYMMDDHHMMSSZ");
346 chunk_t date
= { optarg
, 15 };
348 notBefore
= asn1totime(&date
, ASN1_GENERALIZEDTIME
);
352 case 'E': /* --enddate */
353 if (optarg
== NULL
|| strlen(optarg
) != 15 || optarg
[14] != 'Z')
355 usage("date format must be YYYYMMDDHHMMSSZ");
359 chunk_t date
= { optarg
, 15 };
360 notAfter
= asn1totime(&date
, ASN1_GENERALIZEDTIME
);
364 case 'o': /* --outt */
369 case 'A': /* --debug-all */
370 base_debugging
= DBG_ALL
;
381 usage("unexpected argument");
384 /* load the signer's RSA private key */
387 signerkey
= rsa_private_key_create_from_file(keyfile
, &passphrase
);
389 if (signerkey
== NULL
)
395 /* load the signer's X.509 certificate */
396 if (certfile
!= NULL
)
398 signercert
= x509_create_from_file(certfile
, "signer cert");
400 if (signercert
== NULL
)
406 /* load the users's X.509 certificate */
407 if (usercertfile
!= NULL
)
409 usercert
= x509_create_from_file(usercertfile
, "signer cert");
410 if (usercert
== NULL
)
416 /* compute validity interval */
417 validity
= (validity
)? validity
: default_validity
;
418 notBefore
= (notBefore
) ? notBefore
: time(NULL
);
419 notAfter
= (notAfter
) ? notAfter
: notBefore
+ validity
;
421 /* build and parse attribute certificate */
422 if (usercert
!= NULL
&& signercert
!= NULL
&& signerkey
!= NULL
)
424 /* read the serial number and increment it by one */
425 serial
= read_serial();
427 attr_cert
= build_attr_cert();
428 ac
= x509ac_create_from_chunk(attr_cert
);
430 /* write the attribute certificate to file */
431 if (chunk_write(attr_cert
, outfile
, "attribute cert", 0022, TRUE
))
433 write_serial(serial
);
437 /* delete all dynamically allocated objects */
438 DESTROY_IF(signerkey
);
439 DESTROY_IF(signercert
);
440 DESTROY_IF(usercert
);
442 ietfAttr_list_destroy(groups
);