Skip to content

Commit 8e99bcb

Browse files
committed
Implement reading LM/NT hashes
Fixes imports of NT hashes (hex_to_key was broken) Adds ability to read NTLM_USER_FILE that uses smbpasswd format. (allows to import NT and LM hashes directly) Signed-off-by: Simo Sorce <simo@redhat.com>
1 parent ab1f321 commit 8e99bcb

File tree

1 file changed

+113
-44
lines changed

1 file changed

+113
-44
lines changed

src/gss_creds.c

Lines changed: 113 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,51 @@
2525

2626
#include "gss_ntlmssp.h"
2727

28+
static int hex_to_key(const char *hex, struct ntlm_key *key)
29+
{
30+
size_t len = strlen(hex);
31+
if (len != 32) return ERR_KEYLEN;
32+
33+
for (int i = 0; i < 16; i++) {
34+
key->data[i] = 0;
35+
for (int j = 0; j < 2; j++) {
36+
uint8_t c = hex[i*2+j];
37+
if (c >= '0' && c <= '9') {
38+
c -= '0';
39+
} else if (c >= 'A' && c <= 'F') {
40+
c -= 'A' - 10;
41+
} else if (c >= 'a' && c <= 'f') {
42+
c -= 'a' - 10;
43+
} else {
44+
return ERR_BADARG;
45+
}
46+
key->data[i] |= (c << ((1 - j) * 4));
47+
}
48+
}
49+
key->length = 16;
50+
return 0;
51+
}
52+
2853
static int get_user_file_creds(struct gssntlm_name *name,
2954
struct gssntlm_cred *cred)
3055
{
56+
struct gssntlm_ctx *ctx;
57+
int lm_compat_lvl = -1;
3158
const char *envvar;
3259
char line[1024];
33-
char *dom, *usr, *pwd;
60+
char *field1, *field2, *field3, *field4;
61+
char *dom, *usr, *pwd, *lm, *nt;
3462
char *p;
3563
bool found = false;
3664
FILE *f;
3765
int ret;
3866

67+
ctx = calloc(1, sizeof(struct gssntlm_ctx));
68+
if (!ctx) return ENOMEM;
69+
70+
lm_compat_lvl = gssntlm_get_lm_compatibility_level();
71+
if (!gssntlm_required_security(lm_compat_lvl, ctx)) return ERR_BADLMLVL;
72+
3973
/* use the same var used by Heimdal */
4074
envvar = getenv("NTLM_USER_FILE");
4175
if (envvar == NULL) return ENOENT;
@@ -44,30 +78,78 @@ static int get_user_file_creds(struct gssntlm_name *name,
4478
* some compatibility between implementations:
4579
* Each line is one entry like the following:
4680
* DOMAIN:USERNAME:PASSWORD */
81+
82+
/* **OR** */
83+
84+
/* Use the smbpasswd file format for Samba compatibility.
85+
* Each line is one entry like the following:
86+
* NAME:UID:LM_HASH:NT_HASH:ACCT_FLAGS:TIMESTAMP
87+
* Fields after NT_HASH are ignored
88+
*
89+
* The smbpasswd format is extended to allow domain qualified names.
90+
*/
91+
92+
/* The second format is distinguished from the first based on
93+
* the number of fields encountered. It is technically possible
94+
* to mix both formats in a single file though it is not
95+
* recommended.
96+
*/
97+
4798
f = fopen(envvar, "r");
4899
if (!f) return errno;
49100

50101
while(fgets(line, 1024, f)) {
51102
p = line;
52103
if (*p == '#') continue;
53-
dom = p;
54-
p = strchr(dom, ':');
104+
field1 = p;
105+
p = strchr(field1, ':');
55106
if (!p) continue;
56107
*p++ = '\0';
57-
usr = p;
58-
p = strchr(usr, ':');
108+
field2 = p;
109+
p = strchr(field2, ':');
59110
if (!p) continue;
60111
*p++ = '\0';
61-
pwd = p;
62-
strsep(&p, "\r\n");
112+
field3 = p;
113+
p = strchr(field3, ':');
114+
if (!p) {
115+
/* Assume Heimdal file format */
116+
p = field3;
117+
strsep(&p, "\r\n");
118+
119+
dom = field1;
120+
usr = field2;
121+
pwd = field3;
122+
lm = NULL;
123+
nt = NULL;
124+
} else {
125+
*p++ = '\0';
126+
field4 = p;
127+
p = strchr(field4, ':');
128+
if (!p) continue;
129+
*p++ = '\0';
130+
/* Assume smbpasswd file format */
131+
dom = NULL;
132+
usr = field1;
133+
pwd = NULL;
134+
lm = field3;
135+
nt = field4;
136+
137+
/* check if username is domain qualified */
138+
p = strchr(usr, '\\');
139+
if (p) {
140+
dom = usr;
141+
*p++ = '\0';
142+
usr = p;
143+
}
144+
}
63145

64146
/* if no name is specified use the first found */
65147
if (name == NULL) {
66148
found = true;
67149
break;
68150
}
69151

70-
if (name->data.user.domain) {
152+
if (name->data.user.domain && dom) {
71153
if (!ntlm_casecmp(dom, name->data.user.domain)) continue;
72154
}
73155
if (name->data.user.name) {
@@ -86,19 +168,34 @@ static int get_user_file_creds(struct gssntlm_name *name,
86168

87169
cred->type = GSSNTLM_CRED_USER;
88170
cred->cred.user.user.type = GSSNTLM_NAME_USER;
89-
cred->cred.user.user.data.user.domain = strdup(dom);
90-
if (!cred->cred.user.user.data.user.domain) return ENOMEM;
171+
if (dom) {
172+
cred->cred.user.user.data.user.domain = strdup(dom);
173+
if (!cred->cred.user.user.data.user.domain) return ENOMEM;
174+
}
91175
cred->cred.user.user.data.user.name = strdup(usr);
92176
if (!cred->cred.user.user.data.user.name) return ENOMEM;
93-
cred->cred.user.nt_hash.length = 16;
94177

95-
ret = NTOWFv1(pwd, &cred->cred.user.nt_hash);
96-
if (ret) return ret;
178+
if (pwd) {
179+
cred->cred.user.nt_hash.length = 16;
97180

98-
if (gssntlm_get_lm_compatibility_level() < 3) {
99-
cred->cred.user.lm_hash.length = 16;
100-
ret = LMOWFv1(pwd, &cred->cred.user.lm_hash);
181+
ret = NTOWFv1(pwd, &cred->cred.user.nt_hash);
101182
if (ret) return ret;
183+
184+
if (gssntlm_sec_lm_ok(ctx)) {
185+
cred->cred.user.lm_hash.length = 16;
186+
ret = LMOWFv1(pwd, &cred->cred.user.lm_hash);
187+
if (ret) return ret;
188+
}
189+
}
190+
191+
if (lm && nt) {
192+
ret = hex_to_key(nt, &cred->cred.user.nt_hash);
193+
if (ret) return ret;
194+
195+
if (gssntlm_sec_lm_ok(ctx)) {
196+
ret = hex_to_key(lm, &cred->cred.user.lm_hash);
197+
if (ret) return ret;
198+
}
102199
}
103200

104201
return 0;
@@ -134,34 +231,6 @@ static int get_server_creds(struct gssntlm_name *name,
134231
return ret;
135232
}
136233

137-
static int hex_to_key(const char *hex, struct ntlm_key *key)
138-
{
139-
const char *p;
140-
uint32_t i, j;
141-
uint8_t t;
142-
size_t len;
143-
144-
len = strlen(hex);
145-
if (len != 32) return EINVAL;
146-
147-
for (i = 0; i < 16; i++) {
148-
for (j = 0; j < 2; j++) {
149-
p = &hex[j + (i * 2)];
150-
if (*p >= '0' && *p <= '9') {
151-
t = (*p - '0');
152-
} else if (*p >= 'A' && *p <= 'F') {
153-
t = (*p - 'A' + 10);
154-
} else {
155-
return EINVAL;
156-
}
157-
if (j == 0) t = t << 4;
158-
key->data[i] = t;
159-
}
160-
}
161-
key->length = 16;
162-
return 0;
163-
}
164-
165234
#define GENERIC_CS_PASSWORD "password"
166235
/* To support in future, RC4 Key is NT hash */
167236
#define KRB5_CS_CLI_KEYTAB_URN "client_keytab"

0 commit comments

Comments
 (0)