summaryrefslogtreecommitdiffstats
path: root/src/plugins/preauth/pkinit/pkinit_identity.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/preauth/pkinit/pkinit_identity.c')
-rw-r--r--src/plugins/preauth/pkinit/pkinit_identity.c668
1 files changed, 668 insertions, 0 deletions
diff --git a/src/plugins/preauth/pkinit/pkinit_identity.c b/src/plugins/preauth/pkinit/pkinit_identity.c
new file mode 100644
index 0000000000..227c55d0f8
--- /dev/null
+++ b/src/plugins/preauth/pkinit/pkinit_identity.c
@@ -0,0 +1,668 @@
+/*
+ * COPYRIGHT (C) 2007
+ * THE REGENTS OF THE UNIVERSITY OF MICHIGAN
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pkinit.h"
+
+static void
+free_list(char **list)
+{
+ int i;
+
+ if (list == NULL)
+ return;
+
+ for (i = 0; list[i] != NULL; i++)
+ free(list[i]);
+ free(list);
+}
+
+static krb5_error_code
+copy_list(char ***dst, char **src)
+{
+ int i;
+ char **newlist;
+
+ if (dst == NULL)
+ return EINVAL;
+ *dst = NULL;
+
+ if (src == NULL)
+ return 0;
+
+ for (i = 0; src[i] != NULL; i++);
+
+ newlist = calloc(1, (i + 1) * sizeof(*newlist));
+ if (newlist == NULL)
+ return ENOMEM;
+
+ for (i = 0; src[i] != NULL; i++) {
+ newlist[i] = strdup(src[i]);
+ if (newlist[i] == NULL)
+ goto cleanup;
+ }
+ newlist[i] = NULL;
+ *dst = newlist;
+ return 0;
+cleanup:
+ free_list(newlist);
+ return ENOMEM;
+}
+
+char *
+idtype2string(int idtype)
+{
+ switch(idtype) {
+ case IDTYPE_FILE: return "FILE"; break;
+ case IDTYPE_DIR: return "DIR"; break;
+ case IDTYPE_PKCS11: return "PKCS11"; break;
+ case IDTYPE_PKCS12: return "PKCS12"; break;
+ case IDTYPE_ENVVAR: return "ENV"; break;
+ default: return "INVALID"; break;
+ }
+}
+
+char *
+catype2string(int catype)
+{
+ switch(catype) {
+ case CATYPE_ANCHORS: return "ANCHORS"; break;
+ case CATYPE_INTERMEDIATES: return "INTERMEDIATES"; break;
+ case CATYPE_CRLS: return "CRLS"; break;
+ default: return "INVALID"; break;
+ }
+}
+
+krb5_error_code
+pkinit_init_identity_opts(pkinit_identity_opts **idopts)
+{
+ pkinit_identity_opts *opts = NULL;
+
+ *idopts = NULL;
+ opts = (pkinit_identity_opts *) calloc(1, sizeof(pkinit_identity_opts));
+ if (opts == NULL)
+ return ENOMEM;
+
+ opts->identity = NULL;
+ opts->anchors = NULL;
+ opts->intermediates = NULL;
+ opts->crls = NULL;
+ opts->ocsp = NULL;
+ opts->dn_mapping_file = NULL;
+
+ opts->cert_filename = NULL;
+ opts->key_filename = NULL;
+#ifndef WITHOUT_PKCS11
+ opts->p11_module_name = NULL;
+ opts->slotid = PK_NOSLOT;
+ opts->token_label = NULL;
+ opts->cert_id_string = NULL;
+ opts->cert_label = NULL;
+#endif
+
+ *idopts = opts;
+
+ return 0;
+}
+
+krb5_error_code
+pkinit_dup_identity_opts(pkinit_identity_opts *src_opts,
+ pkinit_identity_opts **dest_opts)
+{
+ pkinit_identity_opts *newopts;
+ krb5_error_code retval;
+
+ *dest_opts = NULL;
+ retval = pkinit_init_identity_opts(&newopts);
+ if (retval)
+ return retval;
+
+ retval = ENOMEM;
+
+ if (src_opts->identity != NULL) {
+ newopts->identity = strdup(src_opts->identity);
+ if (newopts->identity == NULL)
+ goto cleanup;
+ }
+
+ retval = copy_list(&newopts->anchors, src_opts->anchors);
+ if (retval)
+ goto cleanup;
+
+ retval = copy_list(&newopts->intermediates,src_opts->intermediates);
+ if (retval)
+ goto cleanup;
+
+ retval = copy_list(&newopts->crls, src_opts->crls);
+ if (retval)
+ goto cleanup;
+
+ if (src_opts->ocsp != NULL) {
+ newopts->ocsp = strdup(src_opts->ocsp);
+ if (newopts->ocsp == NULL)
+ goto cleanup;
+ }
+
+ if (src_opts->cert_filename != NULL) {
+ newopts->cert_filename = strdup(src_opts->cert_filename);
+ if (newopts->cert_filename == NULL)
+ goto cleanup;
+ }
+
+ if (src_opts->key_filename != NULL) {
+ newopts->key_filename = strdup(src_opts->key_filename);
+ if (newopts->key_filename == NULL)
+ goto cleanup;
+ }
+
+#ifndef WITHOUT_PKCS11
+ if (src_opts->p11_module_name != NULL) {
+ newopts->p11_module_name = strdup(src_opts->p11_module_name);
+ if (newopts->p11_module_name == NULL)
+ goto cleanup;
+ }
+
+ newopts->slotid = src_opts->slotid;
+
+ if (src_opts->token_label != NULL) {
+ newopts->token_label = strdup(src_opts->token_label);
+ if (newopts->token_label == NULL)
+ goto cleanup;
+ }
+
+ if (src_opts->cert_id_string != NULL) {
+ newopts->cert_id_string = strdup(src_opts->cert_id_string);
+ if (newopts->cert_id_string == NULL)
+ goto cleanup;
+ }
+
+ if (src_opts->cert_label != NULL) {
+ newopts->cert_label = strdup(src_opts->cert_label);
+ if (newopts->cert_label == NULL)
+ goto cleanup;
+ }
+#endif
+
+
+ *dest_opts = newopts;
+ return 0;
+cleanup:
+ pkinit_fini_identity_opts(newopts);
+ return retval;
+}
+
+void
+pkinit_fini_identity_opts(pkinit_identity_opts *idopts)
+{
+ if (idopts == NULL)
+ return;
+
+ if (idopts->identity != NULL)
+ free(idopts->identity);
+ free_list(idopts->anchors);
+ free_list(idopts->intermediates);
+ free_list(idopts->crls);
+ free_list(idopts->identity_alt);
+
+ if (idopts->cert_filename != NULL)
+ free(idopts->cert_filename);
+ if (idopts->key_filename != NULL)
+ free(idopts->key_filename);
+#ifndef WITHOUT_PKCS11
+ if (idopts->p11_module_name != NULL)
+ free(idopts->p11_module_name);
+ if (idopts->token_label != NULL)
+ free(idopts->token_label);
+ if (idopts->cert_id_string != NULL)
+ free(idopts->cert_id_string);
+ if (idopts->cert_label != NULL)
+ free(idopts->cert_label);
+#endif
+ free(idopts);
+}
+
+#ifndef WITHOUT_PKCS11
+static krb5_error_code
+parse_pkcs11_options(krb5_context context,
+ pkinit_identity_opts *idopts,
+ const char *residual)
+{
+ char *s, *cp, *vp;
+ krb5_error_code retval = ENOMEM;
+
+ if (residual == NULL || residual[0] == '\0')
+ return 0;
+
+ /* Split string into attr=value substrings */
+ s = strdup(residual);
+ if (s == NULL)
+ return retval;
+
+ for ((cp = strtok(s, ":")); cp; (cp = strtok(NULL, ":"))) {
+ vp = strchr(cp, '=');
+
+ /* If there is no "=", this is a pkcs11 module name */
+ if (vp == NULL) {
+ if (idopts->p11_module_name != NULL)
+ free(idopts->p11_module_name);
+ idopts->p11_module_name = strdup(cp);
+ if (idopts->p11_module_name == NULL)
+ goto cleanup;
+ continue;
+ }
+ *vp++ = '\0';
+ if (!strcmp(cp, "module_name")) {
+ if (idopts->p11_module_name != NULL)
+ free(idopts->p11_module_name);
+ idopts->p11_module_name = strdup(vp);
+ if (idopts->p11_module_name == NULL)
+ goto cleanup;
+ } else if (!strcmp(cp, "slotid")) {
+ long slotid = strtol(vp, NULL, 10);
+ if ((slotid == LONG_MIN || slotid == LONG_MAX) && errno != 0) {
+ retval = EINVAL;
+ goto cleanup;
+ }
+ if ((long) (int) slotid != slotid) {
+ retval = EINVAL;
+ goto cleanup;
+ }
+ idopts->slotid = slotid;
+ } else if (!strcmp(cp, "token")) {
+ if (idopts->token_label != NULL)
+ free(idopts->token_label);
+ idopts->token_label = strdup(vp);
+ if (idopts->token_label == NULL)
+ goto cleanup;
+ } else if (!strcmp(cp, "certid")) {
+ if (idopts->cert_id_string != NULL)
+ free(idopts->cert_id_string);
+ idopts->cert_id_string = strdup(vp);
+ if (idopts->cert_id_string == NULL)
+ goto cleanup;
+ } else if (!strcmp(cp, "certlabel")) {
+ if (idopts->cert_label != NULL)
+ free(idopts->cert_label);
+ idopts->cert_label = strdup(vp);
+ if (idopts->cert_label == NULL)
+ goto cleanup;
+ }
+ }
+ retval = 0;
+cleanup:
+ free(s);
+ return retval;
+}
+#endif
+
+static krb5_error_code
+parse_fs_options(krb5_context context,
+ pkinit_identity_opts *idopts,
+ const char *residual)
+{
+ char *certname, *keyname;
+ krb5_error_code retval = ENOMEM;
+
+ if (residual == NULL || residual[0] == '\0')
+ return 0;
+
+ certname = strdup(residual);
+ if (certname == NULL)
+ goto cleanup;
+
+ certname = strtok(certname, ",");
+ keyname = strtok(NULL, ",");
+
+ idopts->cert_filename = strdup(certname);
+ if (idopts->cert_filename == NULL)
+ goto cleanup;
+
+ idopts->key_filename = strdup(keyname ? keyname : certname);
+ if (idopts->key_filename == NULL)
+ goto cleanup;
+
+ retval = 0;
+cleanup:
+ if (certname != NULL)
+ free(certname);
+ return retval;
+}
+
+static krb5_error_code
+parse_pkcs12_options(krb5_context context,
+ pkinit_identity_opts *idopts,
+ const char *residual)
+{
+ krb5_error_code retval = ENOMEM;
+
+ if (residual == NULL || residual[0] == '\0')
+ return 0;
+
+ idopts->cert_filename = strdup(residual);
+ if (idopts->cert_filename == NULL)
+ goto cleanup;
+
+ idopts->key_filename = strdup(residual);
+ if (idopts->key_filename == NULL)
+ goto cleanup;
+
+ pkiDebug("%s: cert_filename '%s' key_filename '%s'\n",
+ __FUNCTION__, idopts->cert_filename,
+ idopts->key_filename);
+ retval = 0;
+cleanup:
+ return retval;
+}
+
+static krb5_error_code
+process_option_identity(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *value)
+{
+ const char *residual;
+ int idtype;
+ krb5_error_code retval = 0;
+
+ pkiDebug("%s: processing value '%s'\n",
+ __FUNCTION__, value ? value : "NULL");
+ if (value == NULL)
+ return EINVAL;
+
+ residual = strchr(value, ':');
+ if (residual != NULL) {
+ unsigned int typelen;
+ residual++; /* skip past colon */
+ typelen = residual - value;
+ if (strncmp(value, "FILE:", typelen) == 0) {
+ idtype = IDTYPE_FILE;
+#ifndef WITHOUT_PKCS11
+ } else if (strncmp(value, "PKCS11:", typelen) == 0) {
+ idtype = IDTYPE_PKCS11;
+#endif
+ } else if (strncmp(value, "PKCS12:", typelen) == 0) {
+ idtype = IDTYPE_PKCS12;
+ } else if (strncmp(value, "DIR:", typelen) == 0) {
+ idtype = IDTYPE_DIR;
+ } else if (strncmp(value, "ENV:", typelen) == 0) {
+ idtype = IDTYPE_ENVVAR;
+ } else {
+ pkiDebug("%s: Unsupported type while processing '%s'\n",
+ __FUNCTION__, value);
+ krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
+ "Unsupported type while processing '%s'\n",
+ value);
+ return KRB5_PREAUTH_FAILED;
+ }
+ } else {
+ idtype = IDTYPE_FILE;
+ residual = value;
+ }
+
+ idopts->idtype = idtype;
+ pkiDebug("%s: idtype is %s\n", __FUNCTION__, idtype2string(idopts->idtype));
+ switch (idtype) {
+ case IDTYPE_ENVVAR:
+ return process_option_identity(context, plg_cryptoctx, req_cryptoctx,
+ idopts, id_cryptoctx, getenv(residual));
+ break;
+ case IDTYPE_FILE:
+ retval = parse_fs_options(context, idopts, residual);
+ break;
+ case IDTYPE_PKCS12:
+ retval = parse_pkcs12_options(context, idopts, residual);
+ break;
+#ifndef WITHOUT_PKCS11
+ case IDTYPE_PKCS11:
+ retval = parse_pkcs11_options(context, idopts, residual);
+ break;
+#endif
+ case IDTYPE_DIR:
+ idopts->cert_filename = strdup(residual);
+ if (idopts->cert_filename == NULL)
+ retval = ENOMEM;
+ break;
+ default:
+ krb5_set_error_message(context, KRB5_PREAUTH_FAILED,
+ "Internal error parsing X509_user_identity\n");
+ retval = EINVAL;
+ break;
+ }
+ return retval;
+}
+
+static krb5_error_code
+process_option_ca_crl(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ const char *value,
+ int catype)
+{
+ char *residual;
+ unsigned int typelen;
+ int idtype;
+
+ pkiDebug("%s: processing catype %s, value '%s'\n",
+ __FUNCTION__, catype2string(catype), value);
+ residual = strchr(value, ':');
+ if (residual == NULL) {
+ pkiDebug("No type given for '%s'\n", value);
+ return EINVAL;
+ }
+ residual++; /* skip past colon */
+ typelen = residual - value;
+ if (strncmp(value, "FILE:", typelen) == 0) {
+ idtype = IDTYPE_FILE;
+ } else if (strncmp(value, "DIR:", typelen) == 0) {
+ idtype = IDTYPE_DIR;
+ } else {
+ return ENOTSUP;
+ }
+ return crypto_load_cas_and_crls(context,
+ plg_cryptoctx,
+ req_cryptoctx,
+ idopts, id_cryptoctx,
+ idtype, catype, residual);
+}
+
+static krb5_error_code
+pkinit_identity_process_option(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int attr,
+ const char *value)
+{
+ krb5_error_code retval = 0;
+
+ switch (attr) {
+ case PKINIT_ID_OPT_USER_IDENTITY:
+ retval = process_option_identity(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, value);
+ break;
+ case PKINIT_ID_OPT_ANCHOR_CAS:
+ retval = process_option_ca_crl(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx, value,
+ CATYPE_ANCHORS);
+ break;
+ case PKINIT_ID_OPT_INTERMEDIATE_CAS:
+ retval = process_option_ca_crl(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ value, CATYPE_INTERMEDIATES);
+ break;
+ case PKINIT_ID_OPT_CRLS:
+ retval = process_option_ca_crl(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ value, CATYPE_CRLS);
+ break;
+ case PKINIT_ID_OPT_OCSP:
+ retval = ENOTSUP;
+ break;
+ default:
+ retval = EINVAL;
+ break;
+ }
+ return retval;
+}
+
+krb5_error_code
+pkinit_identity_initialize(krb5_context context,
+ pkinit_plg_crypto_context plg_cryptoctx,
+ pkinit_req_crypto_context req_cryptoctx,
+ pkinit_identity_opts *idopts,
+ pkinit_identity_crypto_context id_cryptoctx,
+ int do_matching,
+ krb5_principal princ)
+{
+ krb5_error_code retval = EINVAL;
+ int i;
+
+ pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx);
+ if (idopts == NULL || id_cryptoctx == NULL)
+ goto errout;
+
+ /*
+ * If identity was specified, use that. (For the kdc, this
+ * is specified as pkinit_identity in the kdc.conf. For users,
+ * this is specified on the command line via X509_user_identity.)
+ * If a user did not specify identity on the command line,
+ * then we will try alternatives which may have been specified
+ * in the config file.
+ */
+ if (idopts->identity != NULL) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_USER_IDENTITY,
+ idopts->identity);
+ } else if (idopts->identity_alt != NULL) {
+ for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++)
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_USER_IDENTITY,
+ idopts->identity_alt[i]);
+ } else {
+ pkiDebug("%s: no user identity options specified\n", __FUNCTION__);
+ goto errout;
+ }
+ if (retval)
+ goto errout;
+
+ retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx,
+ idopts, id_cryptoctx, princ);
+ if (retval)
+ goto errout;
+
+ if (do_matching) {
+ retval = pkinit_cert_matching(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx, princ);
+ if (retval) {
+ pkiDebug("%s: No matching certificate found\n", __FUNCTION__);
+ crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx);
+ goto errout;
+ }
+ } else {
+ /* Tell crypto code to use the "default" */
+ retval = crypto_cert_select_default(context, plg_cryptoctx,
+ req_cryptoctx, id_cryptoctx);
+ if (retval) {
+ pkiDebug("%s: Failed while selecting default certificate\n",
+ __FUNCTION__);
+ crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx);
+ goto errout;
+ }
+ }
+
+ retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx,
+ id_cryptoctx);
+ if (retval)
+ goto errout;
+
+ for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_ANCHOR_CAS,
+ idopts->anchors[i]);
+ if (retval)
+ goto errout;
+ }
+ for (i = 0; idopts->intermediates != NULL
+ && idopts->intermediates[i] != NULL; i++) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_INTERMEDIATE_CAS,
+ idopts->intermediates[i]);
+ if (retval)
+ goto errout;
+ }
+ for (i = 0; idopts->crls != NULL && idopts->crls[i] != NULL; i++) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_CRLS,
+ idopts->crls[i]);
+ if (retval)
+ goto errout;
+ }
+ if (idopts->ocsp != NULL) {
+ retval = pkinit_identity_process_option(context, plg_cryptoctx,
+ req_cryptoctx, idopts,
+ id_cryptoctx,
+ PKINIT_ID_OPT_OCSP,
+ idopts->ocsp);
+ if (retval)
+ goto errout;
+ }
+
+errout:
+ return retval;
+}
+