]>
Commit | Line | Data |
---|---|---|
6e785d85 | 1 | /* |
9af66127 | 2 | * (C) 2002,2005 Guido Serassio <guido.serassio@acmeconsulting.it> |
6e785d85 | 3 | * Based on previous work of Francesco Chemolli and Robert Collins |
4 | * Distributed freely under the terms of the GNU General Public License, | |
5 | * version 2. See the file COPYING for licensing details | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
26ac0430 | 11 | |
6e785d85 | 12 | * You should have received a copy of the GNU General Public License |
13 | * along with this program; if not, write to the Free Software | |
14 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
15 | */ | |
16 | ||
17 | typedef unsigned char uchar; | |
18 | ||
19 | #include "util.h" | |
20 | #include "ntlm.h" | |
21 | #if HAVE_CTYPE_H | |
22 | #include <ctype.h> | |
23 | #endif | |
24 | #include <lm.h> | |
25 | #include <ntsecapi.h> | |
26 | ||
27 | /* returns 1 on success, 0 on failure */ | |
28 | int | |
29 | Valid_Group(char *UserName, char *Group) | |
30 | { | |
31 | int result = FALSE; | |
32 | WCHAR wszUserName[UNLEN+1]; // Unicode user name | |
33 | WCHAR wszGroup[GNLEN+1]; // Unicode Group | |
34 | ||
35 | LPLOCALGROUP_USERS_INFO_0 pBuf = NULL; | |
36 | LPLOCALGROUP_USERS_INFO_0 pTmpBuf; | |
37 | DWORD dwLevel = 0; | |
38 | DWORD dwFlags = LG_INCLUDE_INDIRECT; | |
39 | DWORD dwPrefMaxLen = -1; | |
40 | DWORD dwEntriesRead = 0; | |
41 | DWORD dwTotalEntries = 0; | |
42 | NET_API_STATUS nStatus; | |
43 | DWORD i; | |
44 | DWORD dwTotalCount = 0; | |
45 | ||
26ac0430 | 46 | /* Convert ANSI User Name and Group to Unicode */ |
6e785d85 | 47 | |
48 | MultiByteToWideChar(CP_ACP, 0, UserName, | |
26ac0430 AJ |
49 | strlen(UserName) + 1, wszUserName, |
50 | sizeof(wszUserName) / sizeof(wszUserName[0])); | |
6e785d85 | 51 | MultiByteToWideChar(CP_ACP, 0, Group, |
26ac0430 | 52 | strlen(Group) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0])); |
6e785d85 | 53 | |
54 | /* | |
26ac0430 AJ |
55 | * Call the NetUserGetLocalGroups function |
56 | * specifying information level 0. | |
57 | * | |
58 | * The LG_INCLUDE_INDIRECT flag specifies that the | |
59 | * function should also return the names of the local | |
60 | * groups in which the user is indirectly a member. | |
61 | */ | |
62 | nStatus = NetUserGetLocalGroups(NULL, | |
63 | wszUserName, | |
64 | dwLevel, | |
65 | dwFlags, | |
66 | (LPBYTE *) & pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries); | |
67 | /* | |
68 | * If the call succeeds, | |
69 | */ | |
6e785d85 | 70 | if (nStatus == NERR_Success) { |
26ac0430 AJ |
71 | if ((pTmpBuf = pBuf) != NULL) { |
72 | for (i = 0; i < dwEntriesRead; i++) { | |
73 | if (pTmpBuf == NULL) { | |
74 | result = FALSE; | |
75 | break; | |
76 | } | |
77 | if (wcscmp(pTmpBuf->lgrui0_name, wszGroup) == 0) { | |
78 | result = TRUE; | |
79 | break; | |
80 | } | |
81 | pTmpBuf++; | |
82 | dwTotalCount++; | |
83 | } | |
84 | } | |
6e785d85 | 85 | } else |
26ac0430 AJ |
86 | result = FALSE; |
87 | /* | |
88 | * Free the allocated memory. | |
89 | */ | |
6e785d85 | 90 | if (pBuf != NULL) |
26ac0430 | 91 | NetApiBufferFree(pBuf); |
6e785d85 | 92 | return result; |
93 | } | |
94 | ||
95 | ||
96 | char * AllocStrFromLSAStr(LSA_UNICODE_STRING LsaStr) | |
97 | { | |
98 | size_t len; | |
99 | static char * target; | |
100 | ||
101 | len = LsaStr.Length/sizeof(WCHAR) + 1; | |
102 | ||
103 | /* allocate buffer for str + null termination */ | |
104 | safe_free(target); | |
105 | target = (char *)xmalloc(len); | |
106 | if (target == NULL) | |
26ac0430 | 107 | return NULL; |
6e785d85 | 108 | |
109 | /* copy unicode buffer */ | |
110 | WideCharToMultiByte(CP_ACP, 0, LsaStr.Buffer, LsaStr.Length, target, len, NULL, NULL ); | |
111 | ||
112 | /* add null termination */ | |
113 | target[len-1] = '\0'; | |
114 | return target; | |
115 | } | |
116 | ||
117 | ||
118 | char * GetDomainName(void) | |
119 | ||
120 | { | |
121 | LSA_HANDLE PolicyHandle; | |
122 | LSA_OBJECT_ATTRIBUTES ObjectAttributes; | |
123 | NTSTATUS status; | |
124 | PPOLICY_PRIMARY_DOMAIN_INFO ppdiDomainInfo; | |
125 | PWKSTA_INFO_100 pwkiWorkstationInfo; | |
126 | DWORD netret; | |
127 | char * DomainName = NULL; | |
128 | ||
26ac0430 | 129 | /* |
6e785d85 | 130 | * Always initialize the object attributes to all zeroes. |
26ac0430 | 131 | */ |
6e785d85 | 132 | memset(&ObjectAttributes, '\0', sizeof(ObjectAttributes)); |
133 | ||
26ac0430 | 134 | /* |
6e785d85 | 135 | * You need the local workstation name. Use NetWkstaGetInfo at level |
136 | * 100 to retrieve a WKSTA_INFO_100 structure. | |
26ac0430 | 137 | * |
6e785d85 | 138 | * The wki100_computername field contains a pointer to a UNICODE |
139 | * string containing the local computer name. | |
26ac0430 | 140 | */ |
6e785d85 | 141 | netret = NetWkstaGetInfo(NULL, 100, (LPBYTE *)&pwkiWorkstationInfo); |
142 | if (netret == NERR_Success) { | |
26ac0430 AJ |
143 | /* |
144 | * We have the workstation name in: | |
145 | * pwkiWorkstationInfo->wki100_computername | |
146 | * | |
147 | * Next, open the policy object for the local system using | |
148 | * the LsaOpenPolicy function. | |
149 | */ | |
150 | status = LsaOpenPolicy( | |
151 | NULL, | |
152 | &ObjectAttributes, | |
153 | GENERIC_READ | POLICY_VIEW_LOCAL_INFORMATION, | |
154 | &PolicyHandle | |
155 | ); | |
156 | ||
157 | /* | |
158 | * Error checking. | |
159 | */ | |
160 | if (status) { | |
161 | debug("OpenPolicy Error: %ld\n", status); | |
162 | } else { | |
163 | ||
164 | /* | |
165 | * You have a handle to the policy object. Now, get the | |
166 | * domain information using LsaQueryInformationPolicy. | |
167 | */ | |
168 | status = LsaQueryInformationPolicy(PolicyHandle, | |
169 | PolicyPrimaryDomainInformation, | |
170 | (void **)&ppdiDomainInfo); | |
171 | if (status) { | |
172 | debug("LsaQueryInformationPolicy Error: %ld\n", status); | |
173 | } else { | |
174 | ||
175 | /* Get name in useable format */ | |
176 | DomainName = AllocStrFromLSAStr(ppdiDomainInfo->Name); | |
177 | ||
178 | /* | |
179 | * Check the Sid pointer, if it is null, the | |
180 | * workstation is either a stand-alone computer | |
181 | * or a member of a workgroup. | |
182 | */ | |
183 | if (ppdiDomainInfo->Sid) { | |
184 | ||
185 | /* | |
186 | * Member of a domain. Display it in debug mode. | |
187 | */ | |
188 | debug("Member of Domain %s\n",DomainName); | |
189 | } else { | |
190 | DomainName = NULL; | |
6e785d85 | 191 | } |
26ac0430 AJ |
192 | } |
193 | } | |
194 | ||
195 | /* | |
196 | * Clean up all the memory buffers created by the LSA and | |
197 | * Net* APIs. | |
198 | */ | |
199 | NetApiBufferFree(pwkiWorkstationInfo); | |
200 | LsaFreeMemory((LPVOID)ppdiDomainInfo); | |
201 | } else | |
202 | debug("NetWkstaGetInfo Error: %ld\n", netret); | |
6e785d85 | 203 | return DomainName; |
204 | } | |
205 | ||
206 | ||
207 | int ntlm_errno; | |
208 | ||
209 | ||
210 | /* returns NULL on failure, or a pointer to | |
211 | * the user's credentials (domain\\username) | |
212 | * upon success. WARNING. It's pointing to static storage. | |
213 | * In case of problem sets as side-effect ntlm_errno to one of the | |
214 | * codes defined in ntlm.h | |
215 | */ | |
216 | char * | |
217 | ntlm_check_auth(ntlm_authenticate * auth, int auth_length) | |
218 | { | |
219 | int rv; | |
220 | char domain[DNLEN+1]; | |
221 | char user[UNLEN+1]; | |
222 | static char credentials[DNLEN+UNLEN+2]; /* we can afford to waste */ | |
223 | ||
224 | lstring tmp; | |
225 | ||
226 | if (!NTLM_LocalCall) { | |
227 | ||
228 | tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain); | |
229 | ||
230 | if (tmp.str == NULL || tmp.l == 0) { | |
26ac0430 AJ |
231 | debug("No domain supplied. Returning no-auth\n"); |
232 | ntlm_errno = NTLM_BAD_REQUEST; | |
233 | return NULL; | |
6e785d85 | 234 | } |
235 | if (Use_Unicode) { | |
236 | /* copy unicode buffer */ | |
237 | WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) tmp.str, tmp.l, domain, DNLEN, NULL, NULL ); | |
238 | /* add null termination */ | |
239 | domain[tmp.l / sizeof(WCHAR)] = '\0'; | |
240 | } else { | |
241 | if (tmp.l > DNLEN) { | |
26ac0430 AJ |
242 | debug("Domain string exceeds %d bytes, rejecting\n", DNLEN); |
243 | ntlm_errno = NTLM_BAD_REQUEST; | |
244 | return NULL; | |
6e785d85 | 245 | } |
246 | memcpy(domain, tmp.str, tmp.l); | |
247 | domain[tmp.l] = '\0'; | |
248 | } | |
249 | tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user); | |
250 | if (tmp.str == NULL || tmp.l == 0) { | |
26ac0430 AJ |
251 | debug("No username supplied. Returning no-auth\n"); |
252 | ntlm_errno = NTLM_BAD_REQUEST; | |
253 | return NULL; | |
6e785d85 | 254 | } |
255 | if (Use_Unicode) { | |
256 | /* copy unicode buffer */ | |
257 | WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) tmp.str, tmp.l, user, UNLEN, NULL, NULL ); | |
258 | /* add null termination */ | |
259 | user[tmp.l / sizeof(WCHAR)] = '\0'; | |
260 | } else { | |
261 | if (tmp.l > UNLEN) { | |
26ac0430 AJ |
262 | debug("Username string exceeds %d bytes, rejecting\n", UNLEN); |
263 | ntlm_errno = NTLM_BAD_REQUEST; | |
264 | return NULL; | |
6e785d85 | 265 | } |
266 | memcpy(user, tmp.str, tmp.l); | |
267 | user[tmp.l] = '\0'; | |
268 | } | |
269 | debug("checking domain: '%s', user: '%s'\n", domain, user); | |
270 | ||
271 | } else | |
272 | debug("checking local user\n"); | |
273 | ||
274 | rv = SSP_ValidateNTLMCredentials(auth, auth_length, credentials); | |
275 | ||
276 | debug("Login attempt had result %d\n", rv); | |
277 | ||
278 | if (!rv) { /* failed */ | |
26ac0430 AJ |
279 | ntlm_errno = NTLM_SSPI_ERROR; |
280 | return NULL; | |
6e785d85 | 281 | } |
26ac0430 | 282 | |
6e785d85 | 283 | if (UseAllowedGroup) { |
26ac0430 AJ |
284 | if (!Valid_Group(credentials, NTAllowedGroup)) { |
285 | ntlm_errno = NTLM_BAD_NTGROUP; | |
286 | debug("User %s not in allowed Group %s\n", credentials, NTAllowedGroup); | |
287 | return NULL; | |
288 | } | |
6e785d85 | 289 | } |
290 | if (UseDisallowedGroup) { | |
26ac0430 AJ |
291 | if (Valid_Group(credentials, NTDisAllowedGroup)) { |
292 | ntlm_errno = NTLM_BAD_NTGROUP; | |
293 | debug("User %s is in denied Group %s\n", credentials, NTDisAllowedGroup); | |
294 | return NULL; | |
295 | } | |
6e785d85 | 296 | } |
297 | ||
298 | debug("credentials: %s\n", credentials); | |
299 | return credentials; | |
300 | } | |
301 | ||
302 | ||
303 | const char * | |
304 | ntlm_make_negotiate(void) | |
305 | { | |
306 | ntlm_negotiate ne; | |
307 | const char *encoded; | |
308 | memset(&ne, 0, sizeof(ntlm_negotiate)); /* reset */ | |
309 | memcpy(ne.signature, "NTLMSSP", 8); /* set the signature */ | |
310 | ne.type = le32toh(NTLM_NEGOTIATE); /* this is a challenge */ | |
311 | ne.flags = le32toh( | |
26ac0430 AJ |
312 | NEGOTIATE_ALWAYS_SIGN | |
313 | NEGOTIATE_USE_NTLM | | |
314 | NEGOTIATE_USE_LM | | |
315 | NEGOTIATE_ASCII | | |
316 | 0 | |
317 | ); | |
6e785d85 | 318 | encoded = base64_encode_bin((char *) &ne, NEGOTIATE_LENGTH); |
319 | debug("Negotiate packet not supplied - self generated\n"); | |
320 | return encoded; | |
321 | } | |
322 | ||
323 | ||
324 | void hex_dump(void *data, int size) | |
325 | { | |
326 | /* dumps size bytes of *data to stdout. Looks like: | |
327 | * [0000] 75 6E 6B 6E 6F 77 6E 20 | |
328 | * 30 FF 00 00 00 00 39 00 unknown 0.....9. | |
329 | * (in a single line of course) | |
330 | */ | |
331 | ||
332 | if (!data) | |
333 | return; | |
334 | ||
335 | if (debug_enabled) { | |
336 | unsigned char *p = data; | |
337 | unsigned char c; | |
338 | int n; | |
339 | char bytestr[4] = {0}; | |
340 | char addrstr[10] = {0}; | |
341 | char hexstr[ 16*3 + 5] = {0}; | |
342 | char charstr[16*1 + 5] = {0}; | |
e1381638 | 343 | for (n=1; n<=size; n++) { |
6e785d85 | 344 | if (n%16 == 1) { |
345 | /* store address for this line */ | |
346 | snprintf(addrstr, sizeof(addrstr), "%.4x", | |
26ac0430 | 347 | ((unsigned int)p-(unsigned int)data) ); |
6e785d85 | 348 | } |
26ac0430 | 349 | |
6e785d85 | 350 | c = *p; |
e4755e29 | 351 | if (xisalnum(c) == 0) { |
6e785d85 | 352 | c = '.'; |
353 | } | |
354 | ||
355 | /* store hex str (for left side) */ | |
356 | snprintf(bytestr, sizeof(bytestr), "%02X ", *p); | |
357 | strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1); | |
358 | ||
359 | /* store char str (for right side) */ | |
360 | snprintf(bytestr, sizeof(bytestr), "%c", c); | |
361 | strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1); | |
362 | ||
26ac0430 | 363 | if (n%16 == 0) { |
6e785d85 | 364 | /* line completed */ |
365 | fprintf(stderr, "[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr); | |
366 | hexstr[0] = 0; | |
367 | charstr[0] = 0; | |
26ac0430 | 368 | } else if (n%8 == 0) { |
6e785d85 | 369 | /* half line: add whitespaces */ |
370 | strncat(hexstr, " ", sizeof(hexstr)-strlen(hexstr)-1); | |
371 | strncat(charstr, " ", sizeof(charstr)-strlen(charstr)-1); | |
372 | } | |
373 | p++; /* next byte */ | |
374 | } | |
375 | ||
376 | if (strlen(hexstr) > 0) { | |
377 | /* print rest of buffer if not empty */ | |
378 | fprintf(stderr, "[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr); | |
379 | } | |
380 | } | |
381 | } | |
382 |