2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A/*
2N/A * COPYRIGHT (C) 2007
2N/A * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
2N/A * ALL RIGHTS RESERVED
2N/A *
2N/A * Permission is granted to use, copy, create derivative works
2N/A * and redistribute this software and such derivative works
2N/A * for any purpose, so long as the name of The University of
2N/A * Michigan is not used in any advertising or publicity
2N/A * pertaining to the use of distribution of this software
2N/A * without specific, written prior authorization. If the
2N/A * above copyright notice or any other identification of the
2N/A * University of Michigan is included in any copy of any
2N/A * portion of this software, then the disclaimer below must
2N/A * also be included.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
2N/A * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
2N/A * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
2N/A * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
2N/A * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
2N/A * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
2N/A * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
2N/A * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
2N/A * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
2N/A * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
2N/A * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
2N/A * SUCH DAMAGES.
2N/A */
2N/A
2N/A#include <errno.h>
2N/A#include <string.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <dlfcn.h>
2N/A#include <unistd.h>
2N/A#include <dirent.h>
2N/A
2N/A#include "pkinit.h"
2N/A
2N/A/* Solaris Kerberos */
2N/A#include <libintl.h>
2N/A
2N/Astatic void
2N/Afree_list(char **list)
2N/A{
2N/A int i;
2N/A
2N/A if (list == NULL)
2N/A return;
2N/A
2N/A for (i = 0; list[i] != NULL; i++)
2N/A free(list[i]);
2N/A free(list);
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Acopy_list(char ***dst, char **src)
2N/A{
2N/A int i;
2N/A char **newlist;
2N/A
2N/A if (dst == NULL)
2N/A return EINVAL;
2N/A *dst = NULL;
2N/A
2N/A if (src == NULL)
2N/A return 0;
2N/A
2N/A for (i = 0; src[i] != NULL; i++);
2N/A
2N/A newlist = calloc(1, (i + 1) * sizeof(*newlist));
2N/A if (newlist == NULL)
2N/A return ENOMEM;
2N/A
2N/A for (i = 0; src[i] != NULL; i++) {
2N/A newlist[i] = strdup(src[i]);
2N/A if (newlist[i] == NULL)
2N/A goto cleanup;
2N/A }
2N/A newlist[i] = NULL;
2N/A *dst = newlist;
2N/A return 0;
2N/Acleanup:
2N/A free_list(newlist);
2N/A return ENOMEM;
2N/A}
2N/A
2N/Achar *
2N/Aidtype2string(int idtype)
2N/A{
2N/A switch(idtype) {
2N/A case IDTYPE_FILE: return "FILE"; break;
2N/A case IDTYPE_DIR: return "DIR"; break;
2N/A case IDTYPE_PKCS11: return "PKCS11"; break;
2N/A case IDTYPE_PKCS12: return "PKCS12"; break;
2N/A case IDTYPE_ENVVAR: return "ENV"; break;
2N/A default: return "INVALID"; break;
2N/A }
2N/A}
2N/A
2N/Achar *
2N/Acatype2string(int catype)
2N/A{
2N/A switch(catype) {
2N/A case CATYPE_ANCHORS: return "ANCHORS"; break;
2N/A case CATYPE_INTERMEDIATES: return "INTERMEDIATES"; break;
2N/A case CATYPE_CRLS: return "CRLS"; break;
2N/A default: return "INVALID"; break;
2N/A }
2N/A}
2N/A
2N/Akrb5_error_code
2N/Apkinit_init_identity_opts(pkinit_identity_opts **idopts)
2N/A{
2N/A pkinit_identity_opts *opts = NULL;
2N/A
2N/A *idopts = NULL;
2N/A opts = calloc(1, sizeof(pkinit_identity_opts));
2N/A if (opts == NULL)
2N/A return ENOMEM;
2N/A
2N/A opts->identity = NULL;
2N/A opts->anchors = NULL;
2N/A opts->intermediates = NULL;
2N/A opts->crls = NULL;
2N/A opts->ocsp = NULL;
2N/A opts->dn_mapping_file = NULL;
2N/A
2N/A opts->cert_filename = NULL;
2N/A opts->key_filename = NULL;
2N/A#ifndef WITHOUT_PKCS11
2N/A opts->p11_module_name = NULL;
2N/A opts->slotid = PK_NOSLOT;
2N/A opts->token_label = NULL;
2N/A opts->cert_id_string = NULL;
2N/A opts->cert_label = NULL;
2N/A#endif
2N/A
2N/A *idopts = opts;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Apkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
2N/A pkinit_identity_opts **dest_opts)
2N/A{
2N/A pkinit_identity_opts *newopts;
2N/A krb5_error_code retval;
2N/A
2N/A *dest_opts = NULL;
2N/A retval = pkinit_init_identity_opts(&newopts);
2N/A if (retval)
2N/A return retval;
2N/A
2N/A retval = ENOMEM;
2N/A
2N/A if (src_opts->identity != NULL) {
2N/A newopts->identity = strdup(src_opts->identity);
2N/A if (newopts->identity == NULL)
2N/A goto cleanup;
2N/A }
2N/A
2N/A retval = copy_list(&newopts->anchors, src_opts->anchors);
2N/A if (retval)
2N/A goto cleanup;
2N/A
2N/A retval = copy_list(&newopts->intermediates,src_opts->intermediates);
2N/A if (retval)
2N/A goto cleanup;
2N/A
2N/A retval = copy_list(&newopts->crls, src_opts->crls);
2N/A if (retval)
2N/A goto cleanup;
2N/A
2N/A if (src_opts->ocsp != NULL) {
2N/A newopts->ocsp = strdup(src_opts->ocsp);
2N/A if (newopts->ocsp == NULL)
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (src_opts->cert_filename != NULL) {
2N/A newopts->cert_filename = strdup(src_opts->cert_filename);
2N/A if (newopts->cert_filename == NULL)
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (src_opts->key_filename != NULL) {
2N/A newopts->key_filename = strdup(src_opts->key_filename);
2N/A if (newopts->key_filename == NULL)
2N/A goto cleanup;
2N/A }
2N/A
2N/A#ifndef WITHOUT_PKCS11
2N/A if (src_opts->p11_module_name != NULL) {
2N/A newopts->p11_module_name = strdup(src_opts->p11_module_name);
2N/A if (newopts->p11_module_name == NULL)
2N/A goto cleanup;
2N/A }
2N/A
2N/A newopts->slotid = src_opts->slotid;
2N/A
2N/A if (src_opts->token_label != NULL) {
2N/A newopts->token_label = strdup(src_opts->token_label);
2N/A if (newopts->token_label == NULL)
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (src_opts->cert_id_string != NULL) {
2N/A newopts->cert_id_string = strdup(src_opts->cert_id_string);
2N/A if (newopts->cert_id_string == NULL)
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (src_opts->cert_label != NULL) {
2N/A newopts->cert_label = strdup(src_opts->cert_label);
2N/A if (newopts->cert_label == NULL)
2N/A goto cleanup;
2N/A }
2N/A#endif
2N/A
2N/A
2N/A *dest_opts = newopts;
2N/A return 0;
2N/Acleanup:
2N/A pkinit_fini_identity_opts(newopts);
2N/A return retval;
2N/A}
2N/A
2N/Avoid
2N/Apkinit_fini_identity_opts(pkinit_identity_opts *idopts)
2N/A{
2N/A if (idopts == NULL)
2N/A return;
2N/A
2N/A if (idopts->identity != NULL)
2N/A free(idopts->identity);
2N/A free_list(idopts->anchors);
2N/A free_list(idopts->intermediates);
2N/A free_list(idopts->crls);
2N/A free_list(idopts->identity_alt);
2N/A
2N/A free(idopts->cert_filename);
2N/A free(idopts->key_filename);
2N/A#ifndef WITHOUT_PKCS11
2N/A free(idopts->p11_module_name);
2N/A free(idopts->token_label);
2N/A free(idopts->cert_id_string);
2N/A free(idopts->cert_label);
2N/A#endif
2N/A free(idopts);
2N/A}
2N/A
2N/A#ifndef WITHOUT_PKCS11
2N/Astatic krb5_error_code
2N/Aparse_pkcs11_options(krb5_context context,
2N/A pkinit_identity_opts *idopts,
2N/A const char *residual)
2N/A{
2N/A char *s, *cp, *vp, *save;
2N/A krb5_error_code retval = ENOMEM;
2N/A
2N/A if (residual == NULL || residual[0] == '\0')
2N/A return 0;
2N/A
2N/A /* Split string into attr=value substrings */
2N/A s = strdup(residual);
2N/A if (s == NULL)
2N/A return retval;
2N/A
2N/A for (cp = strtok_r(s, ":", &save); cp; cp = strtok_r(NULL, ":", &save)) {
2N/A vp = strchr(cp, '=');
2N/A
2N/A /* If there is no "=", this is a pkcs11 module name */
2N/A if (vp == NULL) {
2N/A free(idopts->p11_module_name);
2N/A idopts->p11_module_name = strdup(cp);
2N/A if (idopts->p11_module_name == NULL)
2N/A goto cleanup;
2N/A continue;
2N/A }
2N/A *vp++ = '\0';
2N/A if (!strcmp(cp, "module_name")) {
2N/A free(idopts->p11_module_name);
2N/A idopts->p11_module_name = strdup(vp);
2N/A if (idopts->p11_module_name == NULL)
2N/A goto cleanup;
2N/A } else if (!strcmp(cp, "slotid")) {
2N/A long slotid = strtol(vp, NULL, 10);
2N/A if ((slotid == LONG_MIN || slotid == LONG_MAX) && errno != 0) {
2N/A retval = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A if ((long) (int) slotid != slotid) {
2N/A retval = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A idopts->slotid = slotid;
2N/A } else if (!strcmp(cp, "token")) {
2N/A free(idopts->token_label);
2N/A idopts->token_label = strdup(vp);
2N/A if (idopts->token_label == NULL)
2N/A goto cleanup;
2N/A } else if (!strcmp(cp, "certid")) {
2N/A free(idopts->cert_id_string);
2N/A idopts->cert_id_string = strdup(vp);
2N/A if (idopts->cert_id_string == NULL)
2N/A goto cleanup;
2N/A } else if (!strcmp(cp, "certlabel")) {
2N/A free(idopts->cert_label);
2N/A idopts->cert_label = strdup(vp);
2N/A if (idopts->cert_label == NULL)
2N/A goto cleanup;
2N/A }
2N/A }
2N/A retval = 0;
2N/Acleanup:
2N/A free(s);
2N/A return retval;
2N/A}
2N/A#endif
2N/A
2N/Astatic krb5_error_code
2N/Aparse_fs_options(krb5_context context,
2N/A pkinit_identity_opts *idopts,
2N/A const char *residual)
2N/A{
2N/A char *certname, *keyname, *save;
2N/A krb5_error_code retval = ENOMEM;
2N/A
2N/A if (residual == NULL || residual[0] == '\0')
2N/A return 0;
2N/A
2N/A certname = strdup(residual);
2N/A if (certname == NULL)
2N/A goto cleanup;
2N/A
2N/A certname = strtok_r(certname, ",", &save);
2N/A keyname = strtok_r(NULL, ",", &save);
2N/A
2N/A idopts->cert_filename = strdup(certname);
2N/A if (idopts->cert_filename == NULL)
2N/A goto cleanup;
2N/A
2N/A idopts->key_filename = strdup(keyname ? keyname : certname);
2N/A if (idopts->key_filename == NULL)
2N/A goto cleanup;
2N/A
2N/A retval = 0;
2N/Acleanup:
2N/A free(certname);
2N/A return retval;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Aparse_pkcs12_options(krb5_context context,
2N/A pkinit_identity_opts *idopts,
2N/A const char *residual)
2N/A{
2N/A krb5_error_code retval = ENOMEM;
2N/A
2N/A if (residual == NULL || residual[0] == '\0')
2N/A return 0;
2N/A
2N/A idopts->cert_filename = strdup(residual);
2N/A if (idopts->cert_filename == NULL)
2N/A goto cleanup;
2N/A
2N/A idopts->key_filename = strdup(residual);
2N/A if (idopts->key_filename == NULL)
2N/A goto cleanup;
2N/A
2N/A pkiDebug("%s: cert_filename '%s' key_filename '%s'\n",
2N/A __FUNCTION__, idopts->cert_filename,
2N/A idopts->key_filename);
2N/A retval = 0;
2N/Acleanup:
2N/A return retval;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Aprocess_option_identity(krb5_context context,
2N/A pkinit_plg_crypto_context plg_cryptoctx,
2N/A pkinit_req_crypto_context req_cryptoctx,
2N/A pkinit_identity_opts *idopts,
2N/A pkinit_identity_crypto_context id_cryptoctx,
2N/A const char *value)
2N/A{
2N/A const char *residual;
2N/A int idtype;
2N/A krb5_error_code retval = 0;
2N/A
2N/A pkiDebug("%s: processing value '%s'\n",
2N/A __FUNCTION__, value ? value : "NULL");
2N/A if (value == NULL)
2N/A return EINVAL;
2N/A
2N/A residual = strchr(value, ':');
2N/A if (residual != NULL) {
2N/A unsigned int typelen;
2N/A residual++; /* skip past colon */
2N/A typelen = residual - value;
2N/A if (strncmp(value, "FILE:", typelen) == 0) {
2N/A idtype = IDTYPE_FILE;
2N/A#ifndef WITHOUT_PKCS11
2N/A } else if (strncmp(value, "PKCS11:", typelen) == 0) {
2N/A idtype = IDTYPE_PKCS11;
2N/A#endif
2N/A } else if (strncmp(value, "PKCS12:", typelen) == 0) {
2N/A idtype = IDTYPE_PKCS12;
2N/A } else if (strncmp(value, "DIR:", typelen) == 0) {
2N/A idtype = IDTYPE_DIR;
2N/A } else if (strncmp(value, "ENV:", typelen) == 0) {
2N/A idtype = IDTYPE_ENVVAR;
2N/A } else {
2N/A pkiDebug("%s: Unsupported type while processing '%s'\n",
2N/A __FUNCTION__, value);
2N/A krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
2N/A "Unsupported type while processing '%s'\n",
2N/A value);
2N/A return KRB5_PREAUTH_FAILED;
2N/A }
2N/A } else {
2N/A idtype = IDTYPE_FILE;
2N/A residual = value;
2N/A }
2N/A
2N/A idopts->idtype = idtype;
2N/A pkiDebug("%s: idtype is %s\n", __FUNCTION__, idtype2string(idopts->idtype));
2N/A switch (idtype) {
2N/A case IDTYPE_ENVVAR: {
2N/A /* Solaris Kerberos: Improved error messages */
2N/A char *envvar = getenv(residual);
2N/A if (envvar == NULL) {
2N/A krb5_set_error_message(context, EINVAL,
2N/A gettext("failed to find environmental variable \'%s\'"),
2N/A residual);
2N/A return EINVAL;
2N/A }
2N/A return process_option_identity(context, plg_cryptoctx,
2N/A req_cryptoctx, idopts, id_cryptoctx,
2N/A envvar);
2N/A /* Solaris Kerberos: not reached */
2N/A }
2N/A case IDTYPE_FILE:
2N/A retval = parse_fs_options(context, idopts, residual);
2N/A break;
2N/A case IDTYPE_PKCS12:
2N/A retval = parse_pkcs12_options(context, idopts, residual);
2N/A break;
2N/A#ifndef WITHOUT_PKCS11
2N/A case IDTYPE_PKCS11:
2N/A retval = parse_pkcs11_options(context, idopts, residual);
2N/A break;
2N/A#endif
2N/A case IDTYPE_DIR:
2N/A idopts->cert_filename = strdup(residual);
2N/A if (idopts->cert_filename == NULL)
2N/A retval = ENOMEM;
2N/A break;
2N/A default:
2N/A krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
2N/A "Internal error parsing X509_user_identity\n");
2N/A retval = EINVAL;
2N/A break;
2N/A }
2N/A return retval;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Aprocess_option_ca_crl(krb5_context context,
2N/A pkinit_plg_crypto_context plg_cryptoctx,
2N/A pkinit_req_crypto_context req_cryptoctx,
2N/A pkinit_identity_opts *idopts,
2N/A pkinit_identity_crypto_context id_cryptoctx,
2N/A const char *value,
2N/A int catype)
2N/A{
2N/A char *residual;
2N/A unsigned int typelen;
2N/A int idtype;
2N/A
2N/A pkiDebug("%s: processing catype %s, value '%s'\n",
2N/A __FUNCTION__, catype2string(catype), value);
2N/A residual = strchr(value, ':');
2N/A if (residual == NULL) {
2N/A pkiDebug("No type given for '%s'\n", value);
2N/A return EINVAL;
2N/A }
2N/A residual++; /* skip past colon */
2N/A typelen = residual - value;
2N/A if (strncmp(value, "FILE:", typelen) == 0) {
2N/A idtype = IDTYPE_FILE;
2N/A } else if (strncmp(value, "DIR:", typelen) == 0) {
2N/A idtype = IDTYPE_DIR;
2N/A } else {
2N/A return ENOTSUP;
2N/A }
2N/A return crypto_load_cas_and_crls(context,
2N/A plg_cryptoctx,
2N/A req_cryptoctx,
2N/A idopts, id_cryptoctx,
2N/A idtype, catype, residual);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Apkinit_identity_initialize(krb5_context context,
2N/A pkinit_plg_crypto_context plg_cryptoctx,
2N/A pkinit_req_crypto_context req_cryptoctx,
2N/A pkinit_identity_opts *idopts,
2N/A pkinit_identity_crypto_context id_cryptoctx,
2N/A int do_matching,
2N/A krb5_principal princ)
2N/A{
2N/A krb5_error_code retval = EINVAL;
2N/A int i;
2N/A
2N/A pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx);
2N/A if (!(princ && krb5_principal_compare_any_realm (context, princ, krb5_anonymous_principal()))) {
2N/A if (idopts == NULL || id_cryptoctx == NULL)
2N/A goto errout;
2N/A
2N/A /*
2N/A * If identity was specified, use that. (For the kdc, this
2N/A * is specified as pkinit_identity in the kdc.conf. For users,
2N/A * this is specified on the command line via X509_user_identity.)
2N/A * If a user did not specify identity on the command line,
2N/A * then we will try alternatives which may have been specified
2N/A * in the config file.
2N/A */
2N/A if (idopts->identity != NULL) {
2N/A retval = process_option_identity(context, plg_cryptoctx,
2N/A req_cryptoctx, idopts,
2N/A id_cryptoctx, idopts->identity);
2N/A } else if (idopts->identity_alt != NULL) {
2N/A for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++) {
2N/A retval = process_option_identity(context, plg_cryptoctx,
2N/A req_cryptoctx, idopts,
2N/A id_cryptoctx,
2N/A idopts->identity_alt[i]);
2N/A }
2N/A } else {
2N/A pkiDebug("%s: no user identity options specified\n", __FUNCTION__);
2N/A goto errout;
2N/A }
2N/A if (retval)
2N/A goto errout;
2N/A
2N/A retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx,
2N/A idopts, id_cryptoctx, princ,
2N/A /* Solaris Kerberos 183resync: 1? */
2N/A 1);
2N/A if (retval)
2N/A goto errout;
2N/A
2N/A if (do_matching) {
2N/A retval = pkinit_cert_matching(context, plg_cryptoctx,
2N/A req_cryptoctx, id_cryptoctx, princ,
2N/A TRUE);
2N/A if (retval) {
2N/A pkiDebug("%s: No matching certificate found\n", __FUNCTION__);
2N/A crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
2N/A id_cryptoctx);
2N/A goto errout;
2N/A }
2N/A } else {
2N/A /* Tell crypto code to use the "default" */
2N/A retval = crypto_cert_select_default(context, plg_cryptoctx,
2N/A req_cryptoctx, id_cryptoctx);
2N/A if (retval) {
2N/A pkiDebug("%s: Failed while selecting default certificate\n",
2N/A __FUNCTION__);
2N/A crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
2N/A id_cryptoctx);
2N/A goto errout;
2N/A }
2N/A }
2N/A
2N/A retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
2N/A id_cryptoctx);
2N/A if (retval)
2N/A goto errout;
2N/A } /* Not anonymous principal */
2N/A
2N/A for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) {
2N/A retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx,
2N/A idopts, id_cryptoctx,
2N/A idopts->anchors[i], CATYPE_ANCHORS);
2N/A if (retval)
2N/A goto errout;
2N/A }
2N/A for (i = 0; idopts->intermediates != NULL
2N/A && idopts->intermediates[i] != NULL; i++) {
2N/A retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx,
2N/A idopts, id_cryptoctx,
2N/A idopts->intermediates[i],
2N/A CATYPE_INTERMEDIATES);
2N/A if (retval)
2N/A goto errout;
2N/A }
2N/A for (i = 0; idopts->crls != NULL && idopts->crls[i] != NULL; i++) {
2N/A retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx,
2N/A idopts, id_cryptoctx, idopts->crls[i],
2N/A CATYPE_CRLS);
2N/A if (retval)
2N/A goto errout;
2N/A }
2N/A if (idopts->ocsp != NULL) {
2N/A retval = ENOTSUP;
2N/A goto errout;
2N/A }
2N/A
2N/Aerrout:
2N/A return retval;
2N/A}