summaryrefslogtreecommitdiffstats
path: root/src/rpc_gss_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc_gss_utils.c')
-rw-r--r--src/rpc_gss_utils.c467
1 files changed, 467 insertions, 0 deletions
diff --git a/src/rpc_gss_utils.c b/src/rpc_gss_utils.c
new file mode 100644
index 0000000..80fc78a
--- /dev/null
+++ b/src/rpc_gss_utils.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2013, Oracle America, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of "Oracle America, Inc." nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <reentrant.h>
+
+#include <rpc/auth_gss.h>
+#include <rpc/rpcsec_gss.h>
+
+#include <gssapi/gssapi_krb5.h>
+
+/* Internal only */
+void rpc_gss_set_error(int);
+void rpc_gss_clear_error(void);
+bool_t rpc_gss_oid_to_mech(rpc_gss_OID, char **);
+
+/*
+ * Return the static "_rpc_gss_error" if we are the main thread
+ * (including non-threaded programs), or if an allocation fails.
+ */
+static rpc_gss_error_t *
+__rpc_gss_error(void)
+{
+ static rpc_gss_error_t _rpc_gss_error = { 0, 0 };
+ extern thread_key_t rg_key;
+ rpc_gss_error_t *result;
+
+ if (rg_key == KEY_INITIALIZER) {
+ static mutex_t _rpc_gss_error_lock = MUTEX_INITIALIZER;
+ int err = 0;
+
+ mutex_lock(&_rpc_gss_error_lock);
+ if (rg_key == KEY_INITIALIZER)
+ err = thr_keycreate(&rg_key, free);
+ mutex_unlock(&_rpc_gss_error_lock);
+
+ if (err != 0)
+ return &_rpc_gss_error;
+ }
+
+ result = thr_getspecific(rg_key);
+ if (result == NULL) {
+ result = calloc(1, sizeof(*result));
+ if (result == NULL)
+ return &_rpc_gss_error;
+
+ if (thr_setspecific(rg_key, result) != 0) {
+ free(result);
+ return &_rpc_gss_error;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * External API: Retrieve thread-specific error values
+ *
+ * error: address of rpc_gss_error_t structure to fill in
+ */
+void
+rpc_gss_get_error(rpc_gss_error_t *result)
+{
+ rpc_gss_error_t *error = __rpc_gss_error();
+
+ result->rpc_gss_error = error->rpc_gss_error;
+ result->system_error = error->system_error;
+}
+
+/*
+ * Internal only: Set thread-specific error value
+ *
+ * system_error: new value for thread's .system_error field.
+ * .rpc_gss_error is always set to RPC_GSS_ER_SYSTEMERROR.
+ */
+void
+rpc_gss_set_error(int system_error)
+{
+ rpc_gss_error_t *error = __rpc_gss_error();
+
+ error->rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
+ error->system_error = system_error;
+}
+
+/*
+ * Internal only: Clear thread-specific error values
+ */
+void
+rpc_gss_clear_error(void)
+{
+ rpc_gss_error_t *error = __rpc_gss_error();
+
+ error->rpc_gss_error = RPC_GSS_ER_SUCCESS;
+ error->system_error = 0;
+}
+
+/*
+ * On Solaris, the GSS-API implementation consults the files
+ * /etc/gss/mech and /etc/gss/qop for a mapping of mechanism
+ * names to OIDs, and QOP names to numbers.
+ *
+ * GNU's GSS-API has no such database. We emulate it here
+ * with a built-in table of supported mechanisms and qops,
+ * since the set of supported mechanisms is unlikely to
+ * change often.
+ */
+
+struct _rpc_gss_qop {
+ char *qi_name;
+ unsigned int qi_num;
+};
+
+struct _rpc_gss_mechanism {
+ char *mi_name;
+ rpc_gss_OID_desc mi_oid;
+ char **mi_qop_names;
+ struct _rpc_gss_qop **mi_qops;
+};
+
+
+static struct _rpc_gss_qop _rpc_gss_qop_default = {
+ .qi_name = "GSS_C_QOP_DEFAULT",
+ .qi_num = GSS_C_QOP_DEFAULT,
+};
+
+static struct _rpc_gss_qop *_rpc_gss_krb5_qops[] = {
+ &_rpc_gss_qop_default,
+ NULL,
+};
+
+static char *_rpc_gss_krb5_qop_names[] = {
+ "GSS_C_QOP_DEFAULT",
+ NULL,
+};
+
+/* GSS_MECH_KRB5_OID: Defined by RFC 1964 */
+static struct _rpc_gss_mechanism _rpc_gss_mech_kerberos_v5 = {
+ .mi_name = "kerberos_v5",
+ .mi_oid = { 9, "\052\206\110\206\367\022\001\002\002" },
+ .mi_qop_names = _rpc_gss_krb5_qop_names,
+ .mi_qops = _rpc_gss_krb5_qops,
+};
+
+/* GSS_KRB5_NT_PRINCIPAL_NAME: Defined by RFC 1964 */
+static struct _rpc_gss_mechanism _rpc_gss_mech_kerberos_v5_princname = {
+ .mi_name = "kerberos_v5",
+ .mi_oid = { 10, "\052\206\110\206\367\022\001\002\002\001" },
+ .mi_qop_names = _rpc_gss_krb5_qop_names,
+ .mi_qops = _rpc_gss_krb5_qops,
+};
+
+static struct _rpc_gss_mechanism *_rpc_gss_mechanisms[] = {
+ &_rpc_gss_mech_kerberos_v5,
+ &_rpc_gss_mech_kerberos_v5_princname,
+ NULL,
+};
+
+static char *_rpc_gss_mechanism_names[] = {
+ "kerberos_v5",
+ NULL,
+};
+
+static struct _rpc_gss_mechanism *
+_rpc_gss_find_mechanism(char *mechanism)
+{
+ unsigned int i;
+
+ for (i = 0; _rpc_gss_mechanisms[i] != NULL; i++)
+ if (strcmp(mechanism, _rpc_gss_mechanisms[i]->mi_name) == 0)
+ return _rpc_gss_mechanisms[i];
+ return NULL;
+}
+
+static bool_t
+_rpc_gss_OID_equal(rpc_gss_OID o1, rpc_gss_OID o2)
+{
+ return (o1->length == o2->length) &&
+ (memcmp(o1->elements, o2->elements, o1->length) == 0);
+}
+
+static struct _rpc_gss_mechanism *
+_rpc_gss_find_oid(rpc_gss_OID oid)
+{
+ unsigned int i;
+
+ for (i = 0; _rpc_gss_mechanisms[i] != NULL; i++)
+ if (_rpc_gss_OID_equal(oid, &_rpc_gss_mechanisms[i]->mi_oid))
+ return _rpc_gss_mechanisms[i];
+ return NULL;
+}
+
+static struct _rpc_gss_qop *
+_rpc_gss_find_qop_by_name(struct _rpc_gss_mechanism *m, char *qop)
+{
+ unsigned int i;
+
+ for (i = 0; m->mi_qops[i] != NULL; i++)
+ if (strcmp(qop, m->mi_qops[i]->qi_name) == 0)
+ return m->mi_qops[i];
+ return NULL;
+}
+
+static struct _rpc_gss_qop *
+_rpc_gss_find_qop_by_num(struct _rpc_gss_mechanism *m, u_int num)
+{
+ unsigned int i;
+
+ for (i = 0; m->mi_qops[i] != NULL; i++)
+ if (num == m->mi_qops[i]->qi_num)
+ return m->mi_qops[i];
+ return NULL;
+}
+
+/*
+ * External API: Return array of security mechanism names
+ *
+ * Returns NULL-terminated array of pointers to NUL-terminated C
+ * strings, each containing a mechanism name. This is statically
+ * allocated memory. Caller must not free this array.
+ */
+char **
+rpc_gss_get_mechanisms(void)
+{
+ rpc_gss_clear_error();
+ return (char **)_rpc_gss_mechanism_names;
+}
+
+/*
+ * External API: Return array of qop names for a security mechanism
+ *
+ * mechanism: NUL-terminated C string containing mechanism name
+ * dummy: address of a service enum
+ *
+ * Returns NULL-terminated array of pointers to NUL-terminated C
+ * strings, each containing a qop name. This is statically
+ * allocated memory. Caller must not free this array.
+ */
+char **
+rpc_gss_get_mech_info(char *mechanism, rpc_gss_service_t *dummy)
+{
+ struct _rpc_gss_mechanism *m;
+
+ if (mechanism == NULL || dummy == NULL) {
+ rpc_gss_set_error(EINVAL);
+ return NULL;
+ }
+
+ m = _rpc_gss_find_mechanism(mechanism);
+ if (m == NULL) {
+ rpc_gss_set_error(ENOENT);
+ return NULL;
+ }
+
+ rpc_gss_clear_error();
+ *dummy = rpcsec_gss_svc_privacy;
+ return (char **)m->mi_qop_names;
+}
+
+/*
+ * External API: Return range of supported RPCSEC_GSS versions
+ *
+ * high: address of highest supported version to fill in
+ * low: address of lowest supported version to fill in
+ *
+ * Returns TRUE if successful, or FALSE if an error occurs.
+ */
+bool_t
+rpc_gss_get_versions(u_int *high, u_int *low)
+{
+ if (high == NULL || low == NULL) {
+ rpc_gss_set_error(EINVAL);
+ return FALSE;
+ }
+
+ rpc_gss_clear_error();
+ *high = *low = RPCSEC_GSS_VERSION;
+ return TRUE;
+}
+
+/*
+ * External API: Check if a security mechanism is supported
+ *
+ * mechanism: NUL-terminated C string containing mechanism name
+ *
+ * Returns TRUE if the mechanism name is recognized and supported,
+ * otherwise FALSE is returned.
+ */
+bool_t
+rpc_gss_is_installed(char *mechanism)
+{
+ struct _rpc_gss_mechanism *m;
+
+ if (mechanism == NULL) {
+ rpc_gss_set_error(EINVAL);
+ return FALSE;
+ }
+
+ rpc_gss_clear_error();
+ m = _rpc_gss_find_mechanism(mechanism);
+ if (m == NULL)
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * External API: Return the OID for a given security mechanism
+ *
+ * mechanism: NUL-terminated C string containing mechanism name
+ * oid: address of OID buffer to fill in
+ *
+ * Returns TRUE if the mechanism name is recognized and supported,
+ * otherwise FALSE is returned.
+ */
+bool_t
+rpc_gss_mech_to_oid(char *mechanism, rpc_gss_OID *result)
+{
+ struct _rpc_gss_mechanism *m;
+
+ if (mechanism == NULL || result == NULL) {
+ rpc_gss_set_error(EINVAL);
+ return FALSE;
+ }
+
+ m = _rpc_gss_find_mechanism(mechanism);
+ if (m == NULL) {
+ rpc_gss_set_error(ENOENT);
+ return FALSE;
+ }
+
+ *result = &m->mi_oid;
+ rpc_gss_clear_error();
+ return TRUE;
+}
+
+/*
+ * Internal only: Return the mechanism name for a given OID
+ *
+ * oid: GSS mechanism OID
+ * mechanism: address of a char * to fill in. This is statically
+ * allocated memory. Caller must not free this memory.
+ *
+ * Returns TRUE if the OID is a recognized and supported mechanism,
+ * otherwise FALSE is returned.
+ */
+bool_t
+rpc_gss_oid_to_mech(rpc_gss_OID oid, char **mechanism)
+{
+ struct _rpc_gss_mechanism *m;
+
+ if (oid == NULL || mechanism == NULL) {
+ rpc_gss_set_error(EINVAL);
+ return FALSE;
+ }
+
+ m = _rpc_gss_find_oid(oid);
+ if (m == NULL) {
+ rpc_gss_set_error(ENOENT);
+ return FALSE;
+ }
+
+ *mechanism = m->mi_name;
+ rpc_gss_clear_error();
+ return TRUE;
+}
+
+/*
+ * External API: Return the QOP number for a given QOP and mechanism name
+ *
+ * qop: NUL-terminated C string containing qop name
+ * mechanism: NUL-terminated C string containing mechanism name
+ * num: address of QOP buffer to fill in
+ *
+ * Returns TRUE if the qop and mechanism name are recognized and
+ * supported, otherwise FALSE is returned.
+ */
+bool_t
+rpc_gss_qop_to_num(char *qop, char *mechanism, u_int *num)
+{
+ struct _rpc_gss_mechanism *m;
+ struct _rpc_gss_qop *q;
+
+ if (qop == NULL || mechanism == NULL || num == NULL) {
+ rpc_gss_set_error(EINVAL);
+ return FALSE;
+ }
+
+ m = _rpc_gss_find_mechanism(mechanism);
+ if (m == NULL)
+ goto out_err;
+
+ q = _rpc_gss_find_qop_by_name(m, qop);
+ if (q == NULL)
+ goto out_err;
+
+ *num = q->qi_num;
+ rpc_gss_clear_error();
+ return TRUE;
+
+out_err:
+ rpc_gss_set_error(ENOENT);
+ return FALSE;
+}
+
+/*
+ * Internal only: Return the QOP name for a given mechanism and QOP number
+ *
+ * mechanism: NUL-terminated C string containing security mechanism name
+ * num: QOP number
+ * qop: address of a char * to fill in. This is statically
+ * allocated memory. Caller must not free this memory.
+ *
+ * Returns TRUE if the QOP and mechanism are recognized and supported,
+ * otherwise FALSE is returned.
+ */
+bool_t
+rpc_gss_num_to_qop(char *mechanism, u_int num, char **qop)
+{
+ struct _rpc_gss_mechanism *m;
+ struct _rpc_gss_qop *q;
+
+ if (mechanism == NULL || qop == NULL) {
+ rpc_gss_set_error(EINVAL);
+ return FALSE;
+ }
+
+ m = _rpc_gss_find_mechanism(mechanism);
+ if (m == NULL)
+ goto out_err;
+
+ q = _rpc_gss_find_qop_by_num(m, num);
+ if (q == NULL)
+ goto out_err;
+
+ *qop = q->qi_name;
+ rpc_gss_clear_error();
+ return TRUE;
+
+out_err:
+ rpc_gss_set_error(ENOENT);
+ return FALSE;
+}