/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <strings.h>
#include <ctype.h>
#include <libgen.h>
#include <libintl.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <kmfapiP.h>
#include "util.h"
/* Supporting structures and global variables for getopt_av(). */
typedef struct av_opts_s {
int shortnm; /* short name character */
char *longnm; /* long name string, NOT terminated */
int longnm_len; /* length of long name string */
boolean_t has_arg; /* takes optional argument */
} av_opts;
static av_opts *opts_av = NULL;
static const char *_save_optstr = NULL;
static int _save_numopts = 0;
int optind_av = 1;
char *optarg_av = NULL;
void
free_policy_list(POLICY_LIST *plist)
{
POLICY_LIST *n = plist, *old;
if (plist == NULL)
return;
while (n != NULL) {
old = n;
kmf_free_policy_record(&n->plc);
n = n->next;
free(old);
}
plist = NULL;
}
int
load_policies(char *file, POLICY_LIST **policy_list)
{
int rv = KC_OK;
KMF_RETURN kmfrv = KMF_OK;
POLICY_LIST *newitem, *plist = NULL;
xmlParserCtxtPtr ctxt;
xmlDocPtr doc = NULL;
xmlNodePtr cur, node;
/* Create a parser context */
ctxt = xmlNewParserCtxt();
if (ctxt == NULL)
return (KMF_ERR_POLICY_DB_FORMAT);
/* Read the policy DB and verify it against the schema. */
doc = xmlCtxtReadFile(ctxt, file, NULL,
XML_PARSE_DTDVALID | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
if (doc == NULL || ctxt->valid == 0) {
kmfrv = KMF_ERR_POLICY_DB_FORMAT;
goto end;
}
cur = xmlDocGetRootElement(doc);
if (cur == NULL) {
kmfrv = KMF_ERR_POLICY_DB_FORMAT;
goto end;
}
node = cur->xmlChildrenNode;
while (node != NULL) {
char *c;
/*
* Search for the policy that matches the given name.
*/
if (!xmlStrcmp((const xmlChar *)node->name,
(const xmlChar *)KMF_POLICY_ELEMENT)) {
/* Check the name attribute */
c = (char *)xmlGetProp(node,
(const xmlChar *)KMF_POLICY_NAME_ATTR);
/* If a match, parse the rest of the data */
if (c != NULL) {
xmlFree(c);
newitem = malloc(sizeof (POLICY_LIST));
if (newitem != NULL) {
(void) memset(newitem, 0,
sizeof (POLICY_LIST));
kmfrv = parsePolicyElement(node,
&newitem->plc);
} else {
kmfrv = KMF_ERR_MEMORY;
goto end;
}
/* add to linked list */
if (plist == NULL) {
plist = newitem;
} else {
POLICY_LIST *n = plist;
while (n->next != NULL)
n = n->next;
n->next = newitem;
newitem->next = NULL;
}
}
}
node = node->next;
}
end:
if (ctxt != NULL)
xmlFreeParserCtxt(ctxt);
if (doc != NULL)
xmlFreeDoc(doc);
if (kmfrv != KMF_OK) {
free_policy_list(plist);
rv = KC_ERR_LOADDB;
} else {
*policy_list = plist;
}
return (rv);
}
/*
* Return 0 if there is any error in the input string.
*/
uint16_t
parseKUlist(char *kustring)
{
uint16_t cur_bit;
uint16_t kubits = 0;
char *p;
p = strtok(kustring, ",");
while (p != NULL) {
cur_bit = kmf_string_to_ku(p);
if (cur_bit == 0) {
kubits = 0;
break;
}
kubits |= cur_bit;
p = strtok(NULL, ",");
}
return (kubits);
}
static void
addToEKUList(KMF_EKU_POLICY *ekus, KMF_OID *newoid)
{
if (newoid != NULL && ekus != NULL) {
ekus->eku_count++;
ekus->ekulist = realloc(
ekus->ekulist, ekus->eku_count * sizeof (KMF_OID));
if (ekus->ekulist != NULL) {
ekus->ekulist[ekus->eku_count-1] = *newoid;
}
}
}
int
parseEKUNames(char *ekulist, KMF_POLICY_RECORD *plc)
{
int rv = KC_OK;
char *p;
KMF_OID *newoid;
KMF_EKU_POLICY *ekus = &plc->eku_set;
if (ekulist == NULL || !strlen(ekulist))
return (0);
/*
* The list should be comma separated list of EKU Names.
*/
p = strtok(ekulist, ",");
/* If no tokens found, then maybe its just a single EKU value */
if (p == NULL) {
newoid = kmf_ekuname_to_oid(ekulist);
if (newoid != NULL) {
addToEKUList(ekus, newoid);
free(newoid);
} else {
rv = KC_ERR_USAGE;
}
}
while (p != NULL) {
newoid = kmf_ekuname_to_oid(p);
if (newoid != NULL) {
addToEKUList(ekus, newoid);
free(newoid);
} else {
rv = KC_ERR_USAGE;
break;
}
p = strtok(NULL, ",");
}
if (rv != KC_OK)
kmf_free_eku_policy(ekus);
return (rv);
}
int
parseEKUOIDs(char *ekulist, KMF_POLICY_RECORD *plc)
{
int rv = KC_OK;
char *p;
KMF_OID newoid = { 0, NULL };
KMF_EKU_POLICY *ekus = &plc->eku_set;
if (ekulist == NULL || !strlen(ekulist))
return (0);
/*
* The list should be comma separated list of EKU Names.
*/
p = strtok(ekulist, ",");
if (p == NULL) {
if (kmf_string_to_oid(ekulist, &newoid) == KMF_OK) {
addToEKUList(ekus, &newoid);
} else {
rv = KC_ERR_USAGE;
}
}
while (p != NULL && rv == 0) {
if (kmf_string_to_oid(p, &newoid) == KMF_OK) {
addToEKUList(ekus, &newoid);
} else {
rv = KC_ERR_USAGE;
break;
}
p = strtok(NULL, ",");
}
if (rv != KC_OK)
kmf_free_eku_policy(ekus);
return (rv);
}
int
get_boolean(char *arg)
{
if (arg == NULL)
return (-1);
if (strcasecmp(arg, "true") == 0)
return (1);
if (strcasecmp(arg, "false") == 0)
return (0);
return (-1);
}
/*
* This function processes the input string. It removes the beginning
* and ending blank's first, makes a copy of the resulting string and
* return it.
*
* This function returns NULL, if there is an error in the
* input string or when the system is out of memory. The output
* "err_flag" argument will record the error code, if it is not NULL.
*/
char *
get_string(char *str, int *err_flag)
{
char *p;
int len, i;
char *retstr = NULL;
if (str == NULL) {
if (err_flag != NULL)
*err_flag = KC_ERR_USAGE;
return (NULL);
}
/* Remove beginning whitespace */
p = str;
while (p != NULL && isspace(*p))
p++;
if (p == NULL) {
if (err_flag != NULL)
*err_flag = KC_ERR_USAGE;
return (NULL);
}
/* Remove the trailing blanks */
len = strlen(p);
while (len > 0 && isspace(p[len-1]))
len--;
if (len == 0) {
if (err_flag != NULL)
*err_flag = KC_ERR_USAGE;
return (NULL);
}
/* Check if there is any non-printable character */
i = 0;
while (i < len) {
if (isprint(p[i]))
i++;
else {
if (err_flag != NULL)
*err_flag = KC_ERR_USAGE;
return (NULL);
}
}
/* Make a copy of the string and return it */
retstr = malloc(len + 1);
if (retstr == NULL) {
if (err_flag != NULL)
*err_flag = KC_ERR_MEMORY;
return (NULL);
}
if (err_flag != NULL)
*err_flag = KC_OK;
(void) strncpy(retstr, p, len);
retstr[len] = '\0';
return (retstr);
}
/*
* Breaks out the getopt-style option string into a structure that can be
* traversed later for calls to getopt_av(). Option string is NOT altered,
* but the struct fields point to locations within option string.
*/
static int
populate_opts(char *optstring)
{
int i;
av_opts *temp;
char *marker;
if (optstring == NULL || *optstring == '\0')
return (0);
/*
* This tries to imitate getopt(3c) Each option must conform to:
* <short name char> [ ':' ] [ '(' <long name string> ')' ]
* If long name is missing, the short name is used for long name.
*/
for (i = 0; *optstring != '\0'; i++) {
if ((temp = (av_opts *)((i == 0) ? malloc(sizeof (av_opts)) :
realloc(opts_av, (i+1) * sizeof (av_opts)))) == NULL) {
free(opts_av);
opts_av = NULL;
return (0);
} else
opts_av = (av_opts *)temp;
marker = optstring; /* may need optstring later */
opts_av[i].shortnm = *marker++; /* set short name */
if (*marker == ':') { /* check for opt arg */
marker++;
opts_av[i].has_arg = B_TRUE;
}
if (*marker == '(') { /* check and set long name */
marker++;
opts_av[i].longnm = marker;
opts_av[i].longnm_len = strcspn(marker, ")");
optstring = marker + opts_av[i].longnm_len + 1;
} else {
/* use short name option character */
opts_av[i].longnm = optstring;
opts_av[i].longnm_len = 1;
optstring = marker;
}
}
return (i);
}
/*
* getopt_av() is very similar to getopt(3c) in that the takes an option
* string, compares command line arguments for matches, and returns a single
* letter option when a match is found. However, getopt_av() differs from
* getopt(3c) by allowing both longname options and values be found
* on the command line.
*/
int
getopt_av(int argc, char * const *argv, const char *optstring)
{
int i;
int len;
if (optind_av >= argc)
return (EOF);
/* First time or when optstring changes from previous one */
if (_save_optstr != optstring) {
if (opts_av != NULL)
free(opts_av);
opts_av = NULL;
_save_optstr = optstring;
_save_numopts = populate_opts((char *)optstring);
}
for (i = 0; i < _save_numopts; i++) {
if (strcmp(argv[optind_av], "--") == 0) {
optind_av++;
break;
}
len = strcspn(argv[optind_av], "=");
if (len == opts_av[i].longnm_len && strncmp(argv[optind_av],
opts_av[i].longnm, opts_av[i].longnm_len) == 0) {
/* matched */
if (!opts_av[i].has_arg) {
optind_av++;
return (opts_av[i].shortnm);
}
/* needs optarg */
if (argv[optind_av][len] == '=') {
optarg_av = &(argv[optind_av][len+1]);
optind_av++;
return (opts_av[i].shortnm);
}
optarg_av = NULL;
optind_av++;
return ((int)'?');
}
}
return (EOF);
}
void
print_sanity_error(KMF_RETURN ret)
{
switch (ret) {
case KMF_ERR_POLICY_NAME:
(void) fprintf(stderr, gettext("Error in the policy name\n"));
break;
case KMF_ERR_TA_POLICY:
(void) fprintf(stderr,
gettext("Error in trust anchor attributes\n"));
break;
case KMF_ERR_OCSP_POLICY:
(void) fprintf(stderr,
gettext("Error in OCSP policy attributes\n"));
break;
default:
break;
}
}
conf_entry_t *
get_keystore_entry(char *kstore_name)
{
conf_entrylist_t *phead = NULL;
conf_entrylist_t *ptr;
conf_entry_t *rtn_entry = NULL;
if (kstore_name == NULL)
return (NULL);
if (get_entrylist(&phead) != KMF_OK)
return (NULL);
ptr = phead;
while (ptr != NULL) {
if (strcmp(ptr->entry->keystore, kstore_name) == 0)
break;
ptr = ptr->next;
}
if (ptr != NULL) /* found the entry */
rtn_entry = dup_entry(ptr->entry);
free_entrylist(phead);
return (rtn_entry);
}