diff options
-rw-r--r-- | src/gss_creds.c | 255 | ||||
-rw-r--r-- | src/gss_ntlmssp.h | 10 | ||||
-rw-r--r-- | src/gss_spi.c | 22 |
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, |