summaryrefslogtreecommitdiffstats
path: root/proxy/src/gp_creds.c
diff options
context:
space:
mode:
Diffstat (limited to 'proxy/src/gp_creds.c')
-rw-r--r--proxy/src/gp_creds.c269
1 files changed, 269 insertions, 0 deletions
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;
+}