]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/ntlm_auth/mswin_sspi/libntlmssp.c
SourceFormat: enforcement
[thirdparty/squid.git] / helpers / ntlm_auth / mswin_sspi / libntlmssp.c
1 /*
2 * (C) 2002,2005 Guido Serassio <guido.serassio@acmeconsulting.it>
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.
11
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
46 /* Convert ANSI User Name and Group to Unicode */
47
48 MultiByteToWideChar(CP_ACP, 0, UserName,
49 strlen(UserName) + 1, wszUserName,
50 sizeof(wszUserName) / sizeof(wszUserName[0]));
51 MultiByteToWideChar(CP_ACP, 0, Group,
52 strlen(Group) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0]));
53
54 /*
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 */
70 if (nStatus == NERR_Success) {
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 }
85 } else
86 result = FALSE;
87 /*
88 * Free the allocated memory.
89 */
90 if (pBuf != NULL)
91 NetApiBufferFree(pBuf);
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)
107 return NULL;
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
129 /*
130 * Always initialize the object attributes to all zeroes.
131 */
132 memset(&ObjectAttributes, '\0', sizeof(ObjectAttributes));
133
134 /*
135 * You need the local workstation name. Use NetWkstaGetInfo at level
136 * 100 to retrieve a WKSTA_INFO_100 structure.
137 *
138 * The wki100_computername field contains a pointer to a UNICODE
139 * string containing the local computer name.
140 */
141 netret = NetWkstaGetInfo(NULL, 100, (LPBYTE *)&pwkiWorkstationInfo);
142 if (netret == NERR_Success) {
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;
191 }
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);
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) {
231 debug("No domain supplied. Returning no-auth\n");
232 ntlm_errno = NTLM_BAD_REQUEST;
233 return NULL;
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) {
242 debug("Domain string exceeds %d bytes, rejecting\n", DNLEN);
243 ntlm_errno = NTLM_BAD_REQUEST;
244 return NULL;
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) {
251 debug("No username supplied. Returning no-auth\n");
252 ntlm_errno = NTLM_BAD_REQUEST;
253 return NULL;
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) {
262 debug("Username string exceeds %d bytes, rejecting\n", UNLEN);
263 ntlm_errno = NTLM_BAD_REQUEST;
264 return NULL;
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 */
279 ntlm_errno = NTLM_SSPI_ERROR;
280 return NULL;
281 }
282
283 if (UseAllowedGroup) {
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 }
289 }
290 if (UseDisallowedGroup) {
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 }
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(
312 NEGOTIATE_ALWAYS_SIGN |
313 NEGOTIATE_USE_NTLM |
314 NEGOTIATE_USE_LM |
315 NEGOTIATE_ASCII |
316 0
317 );
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};
343 for (n=1; n<=size; n++) {
344 if (n%16 == 1) {
345 /* store address for this line */
346 snprintf(addrstr, sizeof(addrstr), "%.4x",
347 ((unsigned int)p-(unsigned int)data) );
348 }
349
350 c = *p;
351 if (xisalnum(c) == 0) {
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
363 if (n%16 == 0) {
364 /* line completed */
365 fprintf(stderr, "[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr);
366 hexstr[0] = 0;
367 charstr[0] = 0;
368 } else if (n%8 == 0) {
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