summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2013-10-26 18:22:00 -0400
committerSimo Sorce <simo@redhat.com>2014-01-06 21:27:55 -0500
commit23994cf7ebcd83658d03f6b9419c3b496ab4b6c2 (patch)
treefe5bdb403a03d54d4a58ebc132b1693fcc8964e3
parent2525b828319b5159a682da20f016a051b6db85bd (diff)
downloadgss-proxy-ntlmssp.tar.gz
gss-proxy-ntlmssp.tar.xz
gss-proxy-ntlmssp.zip
Add NTLMSSP supportntlmssp
-rw-r--r--proxy/configure.ac3
-rw-r--r--proxy/src/gp_creds.c269
-rw-r--r--proxy/src/gp_rpc_acquire_cred.c14
-rw-r--r--proxy/src/gp_rpc_creds.h7
-rw-r--r--proxy/src/gp_rpc_init_sec_context.c4
-rw-r--r--proxy/src/gp_rpc_process.h2
6 files changed, 295 insertions, 4 deletions
diff --git a/proxy/configure.ac b/proxy/configure.ac
index a0cc4ef..1636526 100644
--- a/proxy/configure.ac
+++ b/proxy/configure.ac
@@ -151,6 +151,9 @@ AC_SUBST([GSSRPC_LIBS])
AC_CHECK_FUNCS([__secure_getenv secure_getenv])
+# GSS-NTLMSSP Support
+AC_CHECK_HEADERS([gssapi/gssapi_ntlmssp.h],,[AC_MSG_WARN([Could not find GSS-NTLMSSP headers])])
+
WITH_INITSCRIPT
if test x$initscript = xsystemd; then
WITH_SYSTEMD_UNIT_DIR
diff --git a/proxy/src/gp_creds.c b/proxy/src/gp_creds.c
index 6b81616..670eb9d 100644
--- a/proxy/src/gp_creds.c
+++ b/proxy/src/gp_creds.c
@@ -34,17 +34,34 @@
#include "gp_rpc_creds.h"
#include "gp_creds.h"
#include "gp_conv.h"
+#include "gp_secrets.h"
+
+#ifdef HAVE_GSSAPI_GSSAPI_NTLMSSP_H
+#include <gssapi/gssapi_ntlmssp.h>
+#else
+#define GSS_NTLMSSP_OID_STRING "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a"
+#define GSS_NTLMSSP_OID_LENGTH 10
+#endif /* HAVE_GSSAPI_GSSAPI_NTLMSSP_H */
+#ifndef GSS_NTLMSSP_CS_DOMAIN
+#define GSS_NTLMSSP_CS_DOMAIN "ntlmssp_domain"
+#endif
+#ifndef GSS_NTLMSSP_CS_PASSWORD
+#define GSS_NTLMSSP_CS_PASSWORD "ntlmssp_password"
+#endif
#define GSS_MECH_KRB5_OID_LENGTH 9
#define GSS_MECH_KRB5_OID "\052\206\110\206\367\022\001\002\002"
gss_OID_desc gp_mech_krb5 = { GSS_MECH_KRB5_OID_LENGTH, GSS_MECH_KRB5_OID };
+gss_OID_desc gp_mech_ntlmssp = { GSS_NTLMSSP_OID_LENGTH,
+ GSS_NTLMSSP_OID_STRING };
struct supported_mechs_map {
int internal_id;
const gss_OID mech;
} supported_mechs_map[] = {
{ GP_CRED_KRB5, &gp_mech_krb5 },
+ { GP_CRED_NTLMSSP, &gp_mech_ntlmssp },
{ 0, NULL }
};
@@ -558,3 +575,255 @@ void gp_filter_flags(struct gp_call_ctx *gpcall, uint32_t *flags)
*flags |= gpcall->service->enforce_flags;
*flags &= ~gpcall->service->filter_flags;
}
+
+static int gp_get_ntlm_uid_creds(uid_t uid, gss_name_t *name,
+ char **_domain, char **_password)
+{
+ char *domain = NULL;
+ char *username = NULL;
+ char *password = NULL;
+ char *ud = NULL;
+ gss_buffer_desc namebuf;
+ uint32_t ret_maj, ret_min;
+ int ret;
+
+ ret = gp_get_uid_creds(uid, &domain, &username, &password);
+ if (ret && ret != ENOENT) {
+ goto done;
+ }
+
+ if (username && username[0]) {
+ if (domain && domain[0]) {
+ /* import domain qualified user name */
+ ret = asprintf(&ud, "%s@%s", username, domain);
+ if (ret == -1) {
+ ret = ENOMEM;
+ goto done;
+ }
+ namebuf.value = ud;
+ namebuf.length = strlen(ud);
+ } else {
+ namebuf.value = username;
+ namebuf.length = strlen(username);
+ }
+ } else {
+ /* Name was not available, let's see if the uid's user name fits
+ * the bill, might work if a password file is used */
+ username = uid_to_name(uid);
+ if (username == NULL) {
+ GPERROR("Failed to get username from uid %d\n", uid);
+ ret = ENOENT;
+ goto done;
+ }
+ namebuf.value = username;
+ namebuf.length = strlen(username);
+ }
+
+ ret_maj = gss_import_name(&ret_min, &namebuf, GSS_C_NT_USER_NAME, name);
+ if (ret_maj) {
+ GPERROR("Failed to import username %s\n", username);
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_domain = domain;
+ *_password = password;
+ ret = 0;
+
+done:
+ strzerofree(ud);
+ strzerofree(username);
+ if (ret) {
+ strzerofree(domain);
+ strzerofree(password);
+ }
+ return ret;
+}
+
+static int gp_get_ntlm_environment(struct gp_call_ctx *gpcall,
+ gssx_name *desired_name,
+ gss_name_t *requested_name,
+ gss_cred_usage_t *cred_usage,
+ gss_key_value_set_desc *cs)
+{
+ struct gp_service *svc;
+ gss_name_t name = GSS_C_NO_NAME;
+ gss_OID_desc name_type;
+ uint32_t ret_maj = 0;
+ uint32_t ret_min = 0;
+ uid_t target_uid;
+ const char *fmtstr;
+ char *domain = NULL;
+ char *password = NULL;
+ const char *p;
+ char *str;
+ int ret = -1;
+ int c, d;
+
+ memset(cs, 0, sizeof(gss_key_value_set_desc));
+
+ target_uid = gp_conn_get_uid(gpcall->connection);
+ svc = gpcall->service;
+
+ /* filter based on cred_usage */
+ if (svc->cred_usage != GSS_C_BOTH) {
+ if (*cred_usage == GSS_C_BOTH) {
+ *cred_usage = svc->cred_usage;
+ } else if (svc->cred_usage != *cred_usage) {
+ ret = EACCES;
+ goto done;
+ }
+ }
+
+ if (desired_name) {
+ gp_conv_gssx_to_oid(&desired_name->name_type, &name_type);
+
+ /* A service retains the trusted flag only if the current uid matches
+ * the configured euid */
+ if (svc->trusted && (svc->euid == target_uid)) {
+ if (gss_oid_equal(&name_type, GSS_C_NT_STRING_UID_NAME) ||
+ gss_oid_equal(&name_type, GSS_C_NT_MACHINE_UID_NAME)) {
+ target_uid = atol(desired_name->display_name.octet_string_val);
+ } else {
+ ret_maj = gp_conv_gssx_to_name(&ret_min, desired_name, &name);
+ if (ret_maj) {
+ ret = ret_min;
+ goto done;
+ }
+ *requested_name = name;
+ }
+ }
+ }
+
+ ret = gp_get_ntlm_uid_creds(target_uid, &name, &domain, &password);
+ if (ret) {
+ goto done;
+ }
+
+ /* allocate 2 more than in source, for domain and password */
+ cs->elements = calloc(svc->cred_store.count + 1,
+ sizeof(gss_key_value_element_desc));
+ if (!cs->elements) {
+ ret = ENOMEM;
+ goto done;
+ }
+ c = 0;
+ for (d = 0; d < svc->cred_store.count; d++) {
+ p = strchr(svc->cred_store.store[d], ':');
+ if (!p) {
+ GPERROR("Invalid cred_store value"
+ "no ':' separator found in [%s].\n",
+ svc->cred_store.store[d]);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (domain && domain[0] &&
+ strncmp(svc->cred_store.store[d],
+ GSS_NTLMSSP_CS_DOMAIN":",
+ sizeof(GSS_NTLMSSP_CS_DOMAIN) == 0)) {
+ /* skip, we are replacing this one */
+ continue;
+ } else if (password && password[0] &&
+ strncmp(svc->cred_store.store[d],
+ GSS_NTLMSSP_CS_PASSWORD":",
+ sizeof(GSS_NTLMSSP_CS_PASSWORD) == 0)) {
+ /* skip, we are replacing this one */
+ continue;
+ } else {
+ ret = asprintf(&str, "%.*s", (int)(p - svc->cred_store.store[d]),
+ svc->cred_store.store[d]);
+ if (ret == -1) {
+ ret = ENOMEM;
+ goto done;
+ }
+ cs->elements[c].key = str;
+
+ fmtstr = p + 1;
+ cs->elements[c].value = get_formatted_string(fmtstr, target_uid);
+ if (!cs->elements[c].value) {
+ GPDEBUG("Failed to format credential store string.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ c++;
+ }
+ if (domain && domain[0]) {
+ cs->elements[c].key = strdup(GSS_NTLMSSP_CS_DOMAIN);
+ if (!cs->elements[c].key) {
+ ret = ENOMEM;
+ goto done;
+ }
+ cs->elements[c].value = domain;
+ domain = NULL;
+ c++;
+ }
+ if (password && password[0]) {
+ cs->elements[c].key = strdup(GSS_NTLMSSP_CS_PASSWORD);
+ if (!cs->elements[c].key) {
+ ret = ENOMEM;
+ goto done;
+ }
+ cs->elements[c].value = password;
+ password = NULL;
+ c++;
+ }
+ cs->count = c;
+
+ ret = 0;
+
+done:
+ strzerofree(domain);
+ strzerofree(password);
+ if (ret) {
+ free_cred_store_elements(cs);
+ }
+ return ret;
+}
+
+uint32_t gp_add_ntlmssp_creds(uint32_t *min,
+ struct gp_call_ctx *gpcall,
+ gss_cred_id_t in_cred,
+ gssx_name *desired_name,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle)
+{
+ uint32_t ret_maj = 0;
+ uint32_t ret_min = 0;
+ uint32_t discard;
+ gss_name_t req_name = GSS_C_NO_NAME;
+ gss_OID_set_desc desired_mechs = { 1, &gp_mech_ntlmssp };
+ gss_key_value_set_desc cred_store;
+
+ if (!min || !output_cred_handle) {
+ return GSS_S_CALL_INACCESSIBLE_WRITE;
+ }
+
+ *min = 0;
+ *output_cred_handle = GSS_C_NO_CREDENTIAL;
+
+ ret_min = gp_get_ntlm_environment(gpcall, desired_name, &req_name,
+ &cred_usage, &cred_store);
+ if (ret_min) {
+ ret_maj = GSS_S_CRED_UNAVAIL;
+ goto done;
+ }
+
+ ret_maj = gss_acquire_cred_from(&ret_min, req_name, GSS_C_INDEFINITE,
+ &desired_mechs, cred_usage,
+ &cred_store, output_cred_handle,
+ NULL, NULL);
+ if (ret_maj) {
+ goto done;
+ }
+
+done:
+ if (ret_maj) {
+ if (*output_cred_handle) {
+ gss_release_cred(&discard, output_cred_handle);
+ }
+ }
+ *min = ret_min;
+ return ret_maj;
+}
diff --git a/proxy/src/gp_rpc_acquire_cred.c b/proxy/src/gp_rpc_acquire_cred.c
index 0f53989..e1707d7 100644
--- a/proxy/src/gp_rpc_acquire_cred.c
+++ b/proxy/src/gp_rpc_acquire_cred.c
@@ -123,12 +123,18 @@ int gp_acquire_cred(struct gp_call_ctx *gpcall,
NULL,
NULL,
NULL);
- if (ret_maj) {
- goto done;
- }
+ } else if (gss_oid_equal(desired_mech, &gp_mech_ntlmssp)) {
+ ret_maj = gp_add_ntlmssp_creds(&ret_min,
+ gpcall,
+ in_cred,
+ aca->desired_name,
+ cred_usage,
+ add_out_cred);
} else {
- /* we support only the krb5 mech for now */
ret_maj = GSS_S_BAD_MECH;
+ ret_min = 0;
+ }
+ if (ret_maj) {
goto done;
}
}
diff --git a/proxy/src/gp_rpc_creds.h b/proxy/src/gp_rpc_creds.h
index 4c8febb..e7f2cac 100644
--- a/proxy/src/gp_rpc_creds.h
+++ b/proxy/src/gp_rpc_creds.h
@@ -49,4 +49,11 @@ uint32_t gp_add_krb5_creds(uint32_t *min,
void gp_filter_flags(struct gp_call_ctx *gpcall, uint32_t *flags);
+uint32_t gp_add_ntlmssp_creds(uint32_t *min,
+ struct gp_call_ctx *gpcall,
+ gss_cred_id_t in_cred,
+ gssx_name *desired_name,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle);
+
#endif /* _GP_RPC_CREDS_H_ */
diff --git a/proxy/src/gp_rpc_init_sec_context.c b/proxy/src/gp_rpc_init_sec_context.c
index 5e5d6f1..5fe85af 100644
--- a/proxy/src/gp_rpc_init_sec_context.c
+++ b/proxy/src/gp_rpc_init_sec_context.c
@@ -109,6 +109,10 @@ int gp_init_sec_context(struct gp_call_ctx *gpcall,
GSS_C_INITIATE,
time_req, 0, &ich,
NULL, NULL, NULL);
+ } else if (gss_oid_equal(mech_type, &gp_mech_ntlmssp)) {
+ ret_maj = gp_add_ntlmssp_creds(&ret_min, gpcall,
+ NULL, NULL,
+ GSS_C_INITIATE, &ich);
} else {
ret_maj = GSS_S_NO_CRED;
ret_min = 0;
diff --git a/proxy/src/gp_rpc_process.h b/proxy/src/gp_rpc_process.h
index 0a9b426..214de0e 100644
--- a/proxy/src/gp_rpc_process.h
+++ b/proxy/src/gp_rpc_process.h
@@ -42,6 +42,8 @@
struct gssproxy_ctx;
struct gp_service;
+extern gss_OID_desc gp_mech_ntlmssp;
+
#define gp_exec_std_args struct gp_call_ctx *gpcall, \
union gp_rpc_arg *arg, \
union gp_rpc_res *res