diff options
author | Simo Sorce <simo@redhat.com> | 2013-10-26 18:22:00 -0400 |
---|---|---|
committer | Simo Sorce <simo@redhat.com> | 2014-01-06 21:27:55 -0500 |
commit | 23994cf7ebcd83658d03f6b9419c3b496ab4b6c2 (patch) | |
tree | fe5bdb403a03d54d4a58ebc132b1693fcc8964e3 | |
parent | 2525b828319b5159a682da20f016a051b6db85bd (diff) | |
download | gss-proxy-ntlmssp.tar.gz gss-proxy-ntlmssp.tar.xz gss-proxy-ntlmssp.zip |
Add NTLMSSP supportntlmssp
-rw-r--r-- | proxy/configure.ac | 3 | ||||
-rw-r--r-- | proxy/src/gp_creds.c | 269 | ||||
-rw-r--r-- | proxy/src/gp_rpc_acquire_cred.c | 14 | ||||
-rw-r--r-- | proxy/src/gp_rpc_creds.h | 7 | ||||
-rw-r--r-- | proxy/src/gp_rpc_init_sec_context.c | 4 | ||||
-rw-r--r-- | proxy/src/gp_rpc_process.h | 2 |
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 |