Skip to content

Commit 9266d37

Browse files
committed
Introduce parse_user_name() function
The formet get_enterprise_name() function wa stoo naive and did not allow to properly recognize enterprise names. The code is now rearchitected under a new helper function named parse_user_name() and the function has a proper truth table that establishes input -> output mappings. Signed-off-by: Simo Sorce <[email protected]>
1 parent ecda1a5 commit 9266d37

File tree

1 file changed

+160
-64
lines changed

1 file changed

+160
-64
lines changed

src/gss_names.c

Lines changed: 160 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,20 @@ static uint32_t string_split(uint32_t *minor_status, char sep,
3131
p = memchr(str, sep, len);
3232
if (!p) return GSSERRS(0, GSS_S_UNAVAILABLE);
3333

34-
if (s1) {
35-
l = p - str;
34+
/* left side */
35+
l = p - str;
36+
if (s1 && l != 0) {
3637
r1 = strndup(str, l);
3738
if (!r1) {
3839
set_GSSERR(ENOMEM);
3940
goto done;
4041
}
4142
}
42-
if (s2) {
43-
p++;
44-
l = len - (p - str);
43+
44+
/* right side */
45+
p++;
46+
l = len - (p - str);
47+
if (s2 && l != 0) {
4548
r2 = strndup(p, l);
4649
if (!r2) {
4750
set_GSSERR(ENOMEM);
@@ -62,43 +65,169 @@ static uint32_t string_split(uint32_t *minor_status, char sep,
6265
return GSSERR();
6366
}
6467

68+
/* Form of names allowed in GSSNTLMSSP now:
69+
*
70+
* Standard Forms:
71+
* foo
72+
* USERNAME: foo
73+
* DOMAIN: <null>
74+
*
75+
* BAR\foo
76+
* USERNAME: foo
77+
* DOMAIN: BAR
78+
*
79+
* foo@BAR
80+
* USERNAME: foo
81+
* DOMAIN: BAR
82+
*
83+
* Enterprise name forms:
84+
* foo\@bar.example.com
85+
* USERNAME: [email protected]
86+
* DOMAIN: <null>
87+
*
88+
* foo\@bar.example.com@BAR
89+
* USERNAME: [email protected]
90+
* DOMAIN: BAR
91+
*
92+
93+
* USERNAME: [email protected]
94+
* DOMAIN: <null>
95+
*
96+
97+
* USERNAME: [email protected]
98+
* DOMAIN: BAR
99+
*
100+
101+
* USERNAME: [email protected]
102+
* DOMAIN: BAR@dom
103+
*
104+
* Invalid forms:
105+
* BAR@dom\@foo..
106+
* DOM\foo\@bar
107+
* foo@bar\@baz
108+
*/
65109
#define MAX_NAME_LEN 1024
66-
static uint32_t get_enterprise_name(uint32_t *minor_status,
67-
const char *str, size_t len,
68-
char **username)
110+
static uint32_t parse_user_name(uint32_t *minor_status,
111+
const char *str, size_t len,
112+
char **domain, char **username)
69113
{
70114
uint32_t retmaj;
71115
uint32_t retmin;
72-
char *buf;
73-
char *e;
116+
char *at, *sep;
74117

75118
if (len > MAX_NAME_LEN) {
76119
return GSSERRS(ERR_NAMETOOLONG, GSS_S_BAD_NAME);
77120
}
78-
buf = alloca(len + 1);
79121

80-
memcpy(buf, str, len);
81-
buf[len] = '\0';
122+
*username = NULL;
123+
*domain = NULL;
82124

83-
e = strstr(buf, "\\@");
84-
if (e) {
85-
/* remove escape */
86-
memmove(e, e + 1, len - (e - buf));
87-
} else {
88-
/* check if domain part contains dot */
89-
e = strchr(buf, '@');
90-
if (e) {
91-
e = strchr(e, '.');
125+
/* let's check if there are '@' or '\' signs */
126+
at = memchr(str, '@', len);
127+
sep = memchr(str, '\\', len);
128+
129+
/* Check if enterprise name first */
130+
if (at && sep) {
131+
/* we may have an enterprise name here */
132+
char strbuf[len + 1];
133+
char *buf = strbuf;
134+
bool domain_handled = false;
135+
136+
/* copy buf to manipulate it */
137+
memcpy(buf, str, len);
138+
buf[len] = '\0';
139+
140+
/* adjust pointers relative to new buffer */
141+
sep = buf + (sep - str);
142+
at = buf + (at - str);
143+
144+
if (sep > at) {
145+
/* domain name contains an '@' sign ... */
146+
if (*(sep + 1) == '@') {
147+
/* invalid case of XXX@YYY\@ZZZ*/
148+
set_GSSERR(EINVAL);
149+
goto done;
150+
}
151+
} else if (at - sep == 1) {
152+
/* it's just a '\@' escape */
153+
/* no leading domain */
154+
sep = NULL;
155+
}
156+
157+
if (sep) {
158+
/* leading domain, copy if domain name is not empty */
159+
domain_handled = true;
160+
161+
/* terminate and copy domain, even if empty */
162+
/* NOTE: this is important for the Windbind integration case
163+
* where we need to tell the machinery to *not* add the default
164+
* domain name, it happens when the domain is NULL. */
165+
*sep = '\0';
166+
*domain = strdup(buf);
167+
if (NULL == *domain) {
168+
set_GSSERR(ENOMEM);
169+
goto done;
170+
}
171+
/* point buf at username part */
172+
len = len - (sep - buf) - 1;
173+
buf = sep + 1;
174+
}
175+
176+
for (at = strchr(buf, '@'); at != NULL; at = strchr(at, '@')) {
177+
if (*(at - 1) == '\\') {
178+
if (domain_handled) {
179+
/* Invalid forms like DOM\foo\@bar or foo@bar\@baz */
180+
free(*domain);
181+
*domain = NULL;
182+
set_GSSERR(EINVAL);
183+
goto done;
184+
}
185+
/* remove escape, moving all including terminating '\0' */
186+
memmove(at - 1, at, len - (at - buf) + 1);
187+
} else if (!domain_handled) {
188+
/* an '@' without escape and no previous
189+
* domain was split out.
190+
* the rest of the string is the domain */
191+
*at = '\0';
192+
*domain = strdup(at + 1);
193+
if (NULL == *domain) {
194+
set_GSSERR(ENOMEM);
195+
goto done;
196+
}
197+
/* note we continue the loop to check if any invalid
198+
* \@ escapes is found in the domain part */
199+
}
200+
at += 1;
92201
}
202+
203+
*username = strdup(buf);
204+
if (NULL == *username) {
205+
set_GSSERR(ENOMEM);
206+
goto done;
207+
}
208+
209+
/* we got an enterprise name, return */
210+
set_GSSERRS(0, GSS_S_COMPLETE);
211+
goto done;
93212
}
94-
if (!e) return GSSERRS(0, GSS_S_UNAVAILABLE);
95213

96-
*username = strdup(buf);
97-
if (NULL == *username) {
98-
set_GSSERR(ENOMEM);
214+
/* Check if in classic DOMAIN\User windows format */
215+
if (sep) {
216+
retmaj = string_split(&retmin, '\\', str, len, domain, username);
99217
goto done;
100218
}
101219

220+
/* else accept a user@domain format too */
221+
if (at) {
222+
retmaj = string_split(&retmin, '@', str, len, username, domain);
223+
goto done;
224+
}
225+
226+
/* finally, take string as simple user name */
227+
*username = strndup(str, len);
228+
if (NULL == *username) {
229+
set_GSSERR(ENOMEM);
230+
}
102231
set_GSSERRS(0, GSS_S_COMPLETE);
103232

104233
done:
@@ -186,44 +315,11 @@ uint32_t gssntlm_import_name_by_mech(uint32_t *minor_status,
186315
} else if (gss_oid_equal(input_name_type, GSS_C_NT_USER_NAME)) {
187316

188317
name->type = GSSNTLM_NAME_USER;
189-
name->data.user.domain = NULL;
190-
191-
/* Check if enterprise name first */
192-
retmaj = get_enterprise_name(&retmin,
193-
input_name_buffer->value,
194-
input_name_buffer->length,
195-
&name->data.user.name);
196-
if ((retmaj == GSS_S_COMPLETE) ||
197-
(retmaj != GSS_S_UNAVAILABLE)) {
198-
goto done;
199-
}
200-
/* Check if in classic DOMAIN\User windows format */
201-
retmaj = string_split(&retmin, '\\',
202-
input_name_buffer->value,
203-
input_name_buffer->length,
204-
&name->data.user.domain,
205-
&name->data.user.name);
206-
if ((retmaj == GSS_S_COMPLETE) ||
207-
(retmaj != GSS_S_UNAVAILABLE)) {
208-
goto done;
209-
}
210-
/* else accept a user@domain format too */
211-
retmaj = string_split(&retmin, '@',
212-
input_name_buffer->value,
213-
input_name_buffer->length,
214-
&name->data.user.name,
215-
&name->data.user.domain);
216-
if ((retmaj == GSS_S_COMPLETE) ||
217-
(retmaj != GSS_S_UNAVAILABLE)) {
218-
goto done;
219-
}
220-
/* finally, take string as simple user name */
221-
name->data.user.name = strndup(input_name_buffer->value,
222-
input_name_buffer->length);
223-
if (!name->data.user.name) {
224-
set_GSSERR(ENOMEM);
225-
}
226-
retmaj = GSS_S_COMPLETE;
318+
retmaj = parse_user_name(&retmin,
319+
input_name_buffer->value,
320+
input_name_buffer->length,
321+
&name->data.user.domain,
322+
&name->data.user.name);
227323
} else if (gss_oid_equal(input_name_type, GSS_C_NT_MACHINE_UID_NAME)) {
228324

229325
name->type = GSSNTLM_NAME_USER;

0 commit comments

Comments
 (0)