summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2013-07-21 00:57:25 -0400
committerSimo Sorce <simo@redhat.com>2013-07-28 13:17:51 -0400
commitba8c09800ef820e4b348c2e034fb9aa602463ff3 (patch)
treeb60e4421f35209965ad2b6936bb8f0794b9ca6b7
parentb523d7877fe2d8391dbc0f0f61d9965fe26423f7 (diff)
downloadgss-ntlmssp-ba8c09800ef820e4b348c2e034fb9aa602463ff3.tar.gz
gss-ntlmssp-ba8c09800ef820e4b348c2e034fb9aa602463ff3.tar.xz
gss-ntlmssp-ba8c09800ef820e4b348c2e034fb9aa602463ff3.zip
Add Credential Store support
This allows a program to feed crdentials directly to GSSAPI from a configuration file, or other means.
-rw-r--r--src/gss_creds.c255
-rw-r--r--src/gss_ntlmssp.h10
-rw-r--r--src/gss_spi.c22
3 files changed, 203 insertions, 84 deletions
diff --git a/src/gss_creds.c b/src/gss_creds.c
index 9b7b467..0372d72 100644
--- a/src/gss_creds.c
+++ b/src/gss_creds.c
@@ -26,10 +26,9 @@
#include "gss_ntlmssp.h"
-static int get_initial_creds(struct gssntlm_name *name,
- struct gssntlm_cred **creds)
+static int get_user_file_creds(struct gssntlm_name *name,
+ struct gssntlm_cred *cred)
{
- struct gssntlm_cred *cred = NULL;
const char *envvar;
char line[1024];
char *dom, *usr, *pwd;
@@ -40,93 +39,150 @@ static int get_initial_creds(struct gssntlm_name *name,
/* use the same var used by Heimdal */
envvar = getenv("NTLM_USER_FILE");
- if (envvar != NULL) {
- /* Use the same file format used by Heimdal in hope to achieve
- * some compatibility between implementations:
- * Each line is one entry like the following:
- * DOMAIN:USERNAME:PASSWORD */
- f = fopen(envvar, "r");
- if (!f) return errno;
-
- while(fgets(line, 1024, f)) {
- p = line;
- if (*p == '#') continue;
- dom = p;
- p = strchr(dom, ':');
- if (!p) continue;
- *p++ = '\0';
- usr = p;
- p = strchr(usr, ':');
- if (!p) continue;
- *p++ = '\0';
- pwd = p;
- strsep(&p, "\r\n");
-
- /* if no name is specified use the first found */
- if (name == NULL) {
- found = true;
- break;
- }
-
- if (name->data.user.domain) {
- if (!ntlm_casecmp(dom, name->data.user.domain)) continue;
- }
- if (name->data.user.name) {
- if (!ntlm_casecmp(usr, name->data.user.name)) continue;
- }
- /* all matched (NULLs in name are wildcards) */
+ if (envvar == NULL) return ENOENT;
+
+ /* Use the same file format used by Heimdal in hope to achieve
+ * some compatibility between implementations:
+ * Each line is one entry like the following:
+ * DOMAIN:USERNAME:PASSWORD */
+ f = fopen(envvar, "r");
+ if (!f) return errno;
+
+ while(fgets(line, 1024, f)) {
+ p = line;
+ if (*p == '#') continue;
+ dom = p;
+ p = strchr(dom, ':');
+ if (!p) continue;
+ *p++ = '\0';
+ usr = p;
+ p = strchr(usr, ':');
+ if (!p) continue;
+ *p++ = '\0';
+ pwd = p;
+ strsep(&p, "\r\n");
+
+ /* if no name is specified use the first found */
+ if (name == NULL) {
found = true;
break;
}
- fclose(f);
+ if (name->data.user.domain) {
+ if (!ntlm_casecmp(dom, name->data.user.domain)) continue;
+ }
+ if (name->data.user.name) {
+ if (!ntlm_casecmp(usr, name->data.user.name)) continue;
+ }
+ /* all matched (NULLs in name are wildcards) */
+ found = true;
+ break;
+ }
- if (found) {
- cred = calloc(1, sizeof(struct gssntlm_cred));
- if (!cred) return errno;
+ fclose(f);
- cred->type = GSSNTLM_CRED_USER;
- cred->cred.user.user.type = GSSNTLM_NAME_USER;
- cred->cred.user.user.data.user.domain = strdup(dom);
- if (!cred->cred.user.user.data.user.domain) {
- ret = ENOMEM;
- goto done;
- }
- cred->cred.user.user.data.user.name = strdup(usr);
- if (!cred->cred.user.user.data.user.name) {
- ret = ENOMEM;
- goto done;
- }
- cred->cred.user.nt_hash.length = 16;
+ if (!found) {
+ return ENOENT;
+ }
- ret = NTOWFv1(pwd, &cred->cred.user.nt_hash);
- if (ret) goto done;
+ cred->type = GSSNTLM_CRED_USER;
+ cred->cred.user.user.type = GSSNTLM_NAME_USER;
+ cred->cred.user.user.data.user.domain = strdup(dom);
+ if (!cred->cred.user.user.data.user.domain) return ENOMEM;
+ cred->cred.user.user.data.user.name = strdup(usr);
+ if (!cred->cred.user.user.data.user.name) return ENOMEM;
+ cred->cred.user.nt_hash.length = 16;
- envvar = getenv("LM_COMPAT_LEVEL");
- if (envvar != NULL) {
- cred->lm_compatibility_level = atoi(envvar);
- } else {
- /* use the most secure setting by default */
- cred->lm_compatibility_level = SEC_LEVEL_MAX;
- }
+ ret = NTOWFv1(pwd, &cred->cred.user.nt_hash);
+ if (ret) return ret;
- if (cred->lm_compatibility_level < 3) {
- cred->cred.user.lm_hash.length = 16;
- ret = LMOWFv1(pwd, &cred->cred.user.lm_hash);
+ envvar = getenv("LM_COMPAT_LEVEL");
+ if (envvar != NULL) {
+ cred->lm_compatibility_level = atoi(envvar);
+ } else {
+ /* use the most secure setting by default */
+ cred->lm_compatibility_level = SEC_LEVEL_MAX;
+ }
+
+ if (cred->lm_compatibility_level < 3) {
+ cred->cred.user.lm_hash.length = 16;
+ ret = LMOWFv1(pwd, &cred->cred.user.lm_hash);
+ if (ret) return ret;
+ }
+
+ return 0;
+}
+
+
+static int hex_to_key(const char *hex, struct ntlm_key *key)
+{
+ const char *p;
+ uint32_t i, j;
+ uint8_t t;
+ size_t len;
+
+ len = strlen(hex);
+ if (len != 32) return EINVAL;
+
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < 2; j++) {
+ p = &hex[j + (i * 2)];
+ if (*p >= '0' && *p <= '9') {
+ t = (*p - '0');
+ } else if (*p >= 'A' && *p <= 'F') {
+ t = (*p - 'A' + 10);
+ } else {
+ return EINVAL;
}
- goto done;
+ if (j == 0) t = t << 4;
+ key->data[i] = t;
}
}
+ key->length = 16;
+ return 0;
+}
- ret = ENOENT;
+#define NTLM_CS_DOMAIN "ntlm:domain"
+#define NTLM_CS_NTHASH "ntlm:nthash"
+#define NTLM_CS_PASSWORD "ntlm:password"
+#define GENERIC_CS_PASSWORD "password"
+/* To support in future, RC4 Key is NT hash */
+#define KRB5_CS_CLI_KEYTAB_URN "client_keytab"
+#define KRB5_CS_KEYTAB_URN "keytab"
+
+static int get_creds_from_store(struct gssntlm_name *name,
+ struct gssntlm_cred *cred,
+ gss_const_key_value_set_t cred_store)
+{
+ uint32_t i;
+ int ret;
-done:
- if (ret) {
- gssntlm_int_release_cred(cred);
- } else {
- *creds = cred;
+ for (i = 0; i < cred_store->count; i++) {
+ if (strcmp(cred_store->elements[i].key, NTLM_CS_DOMAIN) == 0) {
+ /* ignore duplicates */
+ if (cred->cred.user.user.data.user.domain) continue;
+ cred->cred.user.user.data.user.domain =
+ strdup(cred_store->elements[i].value);
+ if (!cred->cred.user.user.data.user.domain) return ENOMEM;
+ }
+ if (strcmp(cred_store->elements[i].key, NTLM_CS_NTHASH) == 0) {
+ /* ignore duplicates */
+ if (cred->cred.user.nt_hash.length) continue;
+ ret = hex_to_key(cred_store->elements[i].value,
+ &cred->cred.user.nt_hash);
+ if (ret) return ret;
+ }
+ if ((strcmp(cred_store->elements[i].key, NTLM_CS_PASSWORD) == 0) ||
+ (strcmp(cred_store->elements[i].key, GENERIC_CS_PASSWORD) == 0)) {
+ if (cred->cred.user.nt_hash.length) continue;
+ cred->cred.user.nt_hash.length = 16;
+ ret = NTOWFv1(cred_store->elements[i].value,
+ &cred->cred.user.nt_hash);
+ if (ret) return ret;
+ }
}
- return ret;
+
+ return 0;
}
static void gssntlm_copy_key(struct ntlm_key *dest, struct ntlm_key *src)
@@ -206,14 +262,15 @@ void gssntlm_int_release_cred(struct gssntlm_cred *cred)
}
}
-uint32_t gssntlm_acquire_cred(uint32_t *minor_status,
- gss_name_t desired_name,
- uint32_t time_req,
- gss_OID_set desired_mechs,
- gss_cred_usage_t cred_usage,
- gss_cred_id_t *output_cred_handle,
- gss_OID_set *actual_mechs,
- uint32_t *time_rec)
+uint32_t gssntlm_acquire_cred_from(uint32_t *minor_status,
+ gss_name_t desired_name,
+ uint32_t time_req,
+ gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_const_key_value_set_t cred_store,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ uint32_t *time_rec)
{
struct gssntlm_cred *cred;
struct gssntlm_name *name;
@@ -222,6 +279,12 @@ uint32_t gssntlm_acquire_cred(uint32_t *minor_status,
name = (struct gssntlm_name *)desired_name;
+ cred = calloc(1, sizeof(struct gssntlm_cred));
+ if (!cred) {
+ retmin = errno;
+ return GSS_S_FAILURE;
+ }
+
if (cred_usage == GSS_C_BOTH || cred_usage == GSS_C_INITIATE) {
if (name != NULL && name->type != GSSNTLM_NAME_USER) {
retmin = EINVAL;
@@ -229,7 +292,11 @@ uint32_t gssntlm_acquire_cred(uint32_t *minor_status,
goto done;
}
- retmin = get_initial_creds(name, &cred);
+ if (cred_store != GSS_C_NO_CRED_STORE) {
+ retmin = get_creds_from_store(name, cred, cred_store);
+ } else {
+ retmin = get_user_file_creds(name, cred);
+ }
if (retmin) {
retmaj = GSS_S_CRED_UNAVAIL;
}
@@ -249,6 +316,26 @@ done:
return retmaj;
}
+uint32_t gssntlm_acquire_cred(uint32_t *minor_status,
+ gss_name_t desired_name,
+ uint32_t time_req,
+ gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ uint32_t *time_rec)
+{
+ return gssntlm_acquire_cred_from(minor_status,
+ desired_name,
+ time_req,
+ desired_mechs,
+ cred_usage,
+ GSS_C_NO_CRED_STORE,
+ output_cred_handle,
+ actual_mechs,
+ time_rec);
+}
+
uint32_t gssntlm_release_cred(uint32_t *minor_status,
gss_cred_id_t *cred_handle)
{
diff --git a/src/gss_ntlmssp.h b/src/gss_ntlmssp.h
index ed2b105..334351a 100644
--- a/src/gss_ntlmssp.h
+++ b/src/gss_ntlmssp.h
@@ -146,6 +146,16 @@ uint32_t gssntlm_acquire_cred(uint32_t *minor_status,
gss_OID_set *actual_mechs,
uint32_t *time_rec);
+uint32_t gssntlm_acquire_cred_from(uint32_t *minor_status,
+ gss_name_t desired_name,
+ uint32_t time_req,
+ gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_const_key_value_set_t cred_store,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ uint32_t *time_rec);
+
uint32_t gssntlm_release_cred(uint32_t *minor_status,
gss_cred_id_t *cred_handle);
diff --git a/src/gss_spi.c b/src/gss_spi.c
index 06098a5..2f37b60 100644
--- a/src/gss_spi.c
+++ b/src/gss_spi.c
@@ -16,6 +16,7 @@
*/
#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
#include "gss_ntlmssp.h"
@@ -57,6 +58,27 @@ OM_uint32 gss_delete_sec_context(OM_uint32 *minor_status,
output_token);
}
+OM_uint32 gss_acquire_cred_from(OM_uint32 *minor_status,
+ gss_name_t desired_name,
+ OM_uint32 time_req,
+ gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_const_key_value_set_t cred_store,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec)
+{
+ return gssntlm_acquire_cred_from(minor_status,
+ desired_name,
+ time_req,
+ desired_mechs,
+ cred_usage,
+ cred_store,
+ output_cred_handle,
+ actual_mechs,
+ time_rec);
+}
+
OM_uint32 gss_acquire_cred(OM_uint32 *minor_status,
gss_name_t desired_name,
OM_uint32 time_req,