blob: 24b18bf5c4d26e09e51ead6e661d7419ced09e52 [file] [log] [blame]
/*
* Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#include <ipxe/x509.h>
#include <ipxe/certstore.h>
#include <ipxe/image.h>
#include <ipxe/command.h>
#include <ipxe/parseopt.h>
#include <usr/imgmgmt.h>
#include <usr/certmgmt.h>
/** @file
*
* Certificate management commands
*
*/
/** "cert<xxx>" options */
struct cert_options {
/** Certificate subject name */
char *name;
/** Keep certificate file after parsing */
int keep;
};
/** "cert<xxx>" option list */
static union {
/* "certstore" takes both options */
struct option_descriptor certstore[2];
/* "certstat" takes only --subject */
struct option_descriptor certstat[1];
/* "certfree" takes only --subject */
struct option_descriptor certfree[1];
} opts = {
.certstore = {
OPTION_DESC ( "subject", 's', required_argument,
struct cert_options, name, parse_string ),
OPTION_DESC ( "keep", 'k', no_argument,
struct cert_options, keep, parse_flag ),
},
};
/** A "cert<xxx>" command descriptor */
struct cert_command_descriptor {
/** Command descriptor */
struct command_descriptor cmd;
/** Payload
*
* @v cert X.509 certificate
* @ret rc Return status code
*/
int ( * payload ) ( struct x509_certificate *cert );
};
/**
* Construct "cert<xxx>" command descriptor
*
* @v _struct Options structure type
* @v _options Option descriptor array
* @v _min_args Minimum number of non-option arguments
* @v _max_args Maximum number of non-option arguments
* @v _usage Command usage
* @v _payload Payload method
* @ret _command Command descriptor
*/
#define CERT_COMMAND_DESC( _struct, _options, _min_args, _max_args, \
_usage, _payload ) \
{ \
.cmd = COMMAND_DESC ( _struct, _options, _min_args, \
_max_args, _usage ), \
.payload = _payload, \
}
/**
* Execute "cert<xxx>" command
*
* @v argc Argument count
* @v argv Argument list
* @v certcmd Command descriptor
* @ret rc Return status code
*/
static int cert_exec ( int argc, char **argv,
struct cert_command_descriptor *certcmd ) {
struct command_descriptor *cmd = &certcmd->cmd;
struct cert_options opts;
struct image *image = NULL;
struct x509_certificate *cert;
struct x509_certificate *tmp;
unsigned int count = 0;
size_t offset = 0;
int next;
int rc;
/* Parse options */
if ( ( rc = parse_options ( argc, argv, cmd, &opts ) ) != 0 )
goto err_parse;
/* Acquire image, if applicable */
if ( ( optind < argc ) &&
( ( rc = imgacquire ( argv[optind], 0, &image ) ) != 0 ) )
goto err_acquire;
/* Get first entry in certificate store */
tmp = list_first_entry ( &certstore.links, struct x509_certificate,
store.list );
/* Iterate over certificates */
while ( 1 ) {
/* Get next certificate from image or store as applicable */
if ( image ) {
/* Get next certificate from image */
if ( offset >= image->len )
break;
next = image_x509 ( image, offset, &cert );
if ( next < 0 ) {
rc = next;
printf ( "Could not parse certificate: %s\n",
strerror ( rc ) );
goto err_x509;
}
offset = next;
} else {
/* Get next certificate from store */
cert = tmp;
if ( ! cert )
break;
tmp = list_next_entry ( tmp, &certstore.links,
store.list );
x509_get ( cert );
}
/* Skip non-matching names, if a name was specified */
if ( opts.name && ( x509_check_name ( cert, opts.name ) != 0 )){
x509_put ( cert );
continue;
}
/* Execute payload */
if ( ( rc = certcmd->payload ( cert ) ) != 0 ) {
x509_put ( cert );
goto err_payload;
}
/* Count number of certificates processed */
count++;
/* Drop reference to certificate */
x509_put ( cert );
}
/* Fail if a name was specified and no matching certificates
* were found.
*/
if ( opts.name && ( count == 0 ) ) {
printf ( "\"%s\" : no such certificate\n", opts.name );
rc = -ENOENT;
goto err_none;
}
err_none:
err_payload:
err_x509:
if ( image && ( ! opts.keep ) )
unregister_image ( image );
err_acquire:
err_parse:
return rc;
}
/**
* "certstat" payload
*
* @v cert X.509 certificate
* @ret rc Return status code
*/
static int certstat_payload ( struct x509_certificate *cert ) {
certstat ( cert );
return 0;
}
/** "certstat" command descriptor */
static struct cert_command_descriptor certstat_cmd =
CERT_COMMAND_DESC ( struct cert_options, opts.certstat, 0, 0, NULL,
certstat_payload );
/**
* The "certstat" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int certstat_exec ( int argc, char **argv ) {
return cert_exec ( argc, argv, &certstat_cmd );
}
/**
* "certstore" payload
*
* @v cert X.509 certificate
* @ret rc Return status code
*/
static int certstore_payload ( struct x509_certificate *cert ) {
/* Mark certificate as having been added explicitly */
cert->flags |= X509_FL_EXPLICIT;
return 0;
}
/** "certstore" command descriptor */
static struct cert_command_descriptor certstore_cmd =
CERT_COMMAND_DESC ( struct cert_options, opts.certstore, 0, 1,
"[<uri|image>]", certstore_payload );
/**
* The "certstore" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int certstore_exec ( int argc, char **argv ) {
return cert_exec ( argc, argv, &certstore_cmd );
}
/**
* "certfree" payload
*
* @v cert X.509 certificate
* @ret rc Return status code
*/
static int certfree_payload ( struct x509_certificate *cert ) {
/* Remove from certificate store */
certstore_del ( cert );
return 0;
}
/** "certfree" command descriptor */
static struct cert_command_descriptor certfree_cmd =
CERT_COMMAND_DESC ( struct cert_options, opts.certfree, 0, 0, NULL,
certfree_payload );
/**
* The "certfree" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int certfree_exec ( int argc, char **argv ) {
return cert_exec ( argc, argv, &certfree_cmd );
}
/** Certificate management commands */
struct command certmgmt_commands[] __command = {
{
.name = "certstat",
.exec = certstat_exec,
},
{
.name = "certstore",
.exec = certstore_exec,
},
{
.name = "certfree",
.exec = certfree_exec,
},
};