1 | /*
|
---|
2 | Unix SMB/Netbios implementation.
|
---|
3 | Version 3.0
|
---|
4 | handle NLTMSSP, client server side parsing
|
---|
5 |
|
---|
6 | Copyright (C) Andrew Tridgell 2001
|
---|
7 | Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
---|
8 | Copyright (C) Stefan Metzmacher 2005
|
---|
9 |
|
---|
10 | This program is free software; you can redistribute it and/or modify
|
---|
11 | it under the terms of the GNU General Public License as published by
|
---|
12 | the Free Software Foundation; either version 3 of the License, or
|
---|
13 | (at your option) any later version.
|
---|
14 |
|
---|
15 | This program is distributed in the hope that it will be useful,
|
---|
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
18 | GNU General Public License for more details.
|
---|
19 |
|
---|
20 | You should have received a copy of the GNU General Public License
|
---|
21 | along with this program. If not, see <http://www.gnu.org/licenses/>.
|
---|
22 | */
|
---|
23 |
|
---|
24 | #include "includes.h"
|
---|
25 | #include "system/network.h"
|
---|
26 | #include "auth/ntlmssp/ntlmssp.h"
|
---|
27 | #include "../librpc/gen_ndr/ntlmssp.h"
|
---|
28 | #include "../libcli/auth/libcli_auth.h"
|
---|
29 | #include "../lib/crypto/crypto.h"
|
---|
30 | #include "auth/gensec/gensec.h"
|
---|
31 | #include "auth/auth.h"
|
---|
32 | #include "auth/ntlm/auth_proto.h"
|
---|
33 | #include "param/param.h"
|
---|
34 | #include "auth/session_proto.h"
|
---|
35 |
|
---|
36 | /**
|
---|
37 | * Set a username on an NTLMSSP context - ensures it is talloc()ed
|
---|
38 | *
|
---|
39 | */
|
---|
40 |
|
---|
41 | static NTSTATUS ntlmssp_set_username(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *user)
|
---|
42 | {
|
---|
43 | if (!user) {
|
---|
44 | /* it should be at least "" */
|
---|
45 | DEBUG(1, ("NTLMSSP failed to set username - cannot accept NULL username\n"));
|
---|
46 | return NT_STATUS_INVALID_PARAMETER;
|
---|
47 | }
|
---|
48 | gensec_ntlmssp_state->user = talloc_strdup(gensec_ntlmssp_state, user);
|
---|
49 | if (!gensec_ntlmssp_state->user) {
|
---|
50 | return NT_STATUS_NO_MEMORY;
|
---|
51 | }
|
---|
52 | return NT_STATUS_OK;
|
---|
53 | }
|
---|
54 |
|
---|
55 | /**
|
---|
56 | * Set a domain on an NTLMSSP context - ensures it is talloc()ed
|
---|
57 | *
|
---|
58 | */
|
---|
59 | static NTSTATUS ntlmssp_set_domain(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *domain)
|
---|
60 | {
|
---|
61 | gensec_ntlmssp_state->domain = talloc_strdup(gensec_ntlmssp_state, domain);
|
---|
62 | if (!gensec_ntlmssp_state->domain) {
|
---|
63 | return NT_STATUS_NO_MEMORY;
|
---|
64 | }
|
---|
65 | return NT_STATUS_OK;
|
---|
66 | }
|
---|
67 |
|
---|
68 | /**
|
---|
69 | * Set a workstation on an NTLMSSP context - ensures it is talloc()ed
|
---|
70 | *
|
---|
71 | */
|
---|
72 | static NTSTATUS ntlmssp_set_workstation(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *workstation)
|
---|
73 | {
|
---|
74 | gensec_ntlmssp_state->workstation = talloc_strdup(gensec_ntlmssp_state, workstation);
|
---|
75 | if (!gensec_ntlmssp_state->workstation) {
|
---|
76 | return NT_STATUS_NO_MEMORY;
|
---|
77 | }
|
---|
78 | return NT_STATUS_OK;
|
---|
79 | }
|
---|
80 |
|
---|
81 | /**
|
---|
82 | * Determine correct target name flags for reply, given server role
|
---|
83 | * and negotiated flags
|
---|
84 | *
|
---|
85 | * @param gensec_ntlmssp_state NTLMSSP State
|
---|
86 | * @param neg_flags The flags from the packet
|
---|
87 | * @param chal_flags The flags to be set in the reply packet
|
---|
88 | * @return The 'target name' string.
|
---|
89 | */
|
---|
90 |
|
---|
91 | static const char *ntlmssp_target_name(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
---|
92 | uint32_t neg_flags, uint32_t *chal_flags)
|
---|
93 | {
|
---|
94 | if (neg_flags & NTLMSSP_REQUEST_TARGET) {
|
---|
95 | *chal_flags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
|
---|
96 | *chal_flags |= NTLMSSP_REQUEST_TARGET;
|
---|
97 | if (gensec_ntlmssp_state->server_role == ROLE_STANDALONE) {
|
---|
98 | *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
|
---|
99 | return gensec_ntlmssp_state->server_name;
|
---|
100 | } else {
|
---|
101 | *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
|
---|
102 | return gensec_ntlmssp_state->domain;
|
---|
103 | };
|
---|
104 | } else {
|
---|
105 | return "";
|
---|
106 | }
|
---|
107 | }
|
---|
108 |
|
---|
109 |
|
---|
110 |
|
---|
111 | /**
|
---|
112 | * Next state function for the Negotiate packet
|
---|
113 | *
|
---|
114 | * @param gensec_security GENSEC state
|
---|
115 | * @param out_mem_ctx Memory context for *out
|
---|
116 | * @param in The request, as a DATA_BLOB. reply.data must be NULL
|
---|
117 | * @param out The reply, as an allocated DATA_BLOB, caller to free.
|
---|
118 | * @return Errors or MORE_PROCESSING_REQUIRED if (normal) a reply is required.
|
---|
119 | */
|
---|
120 |
|
---|
121 | NTSTATUS ntlmssp_server_negotiate(struct gensec_security *gensec_security,
|
---|
122 | TALLOC_CTX *out_mem_ctx,
|
---|
123 | const DATA_BLOB in, DATA_BLOB *out)
|
---|
124 | {
|
---|
125 | struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
|
---|
126 | DATA_BLOB struct_blob;
|
---|
127 | uint32_t neg_flags = 0;
|
---|
128 | uint32_t ntlmssp_command, chal_flags;
|
---|
129 | const uint8_t *cryptkey;
|
---|
130 | const char *target_name;
|
---|
131 |
|
---|
132 | /* parse the NTLMSSP packet */
|
---|
133 | #if 0
|
---|
134 | file_save("ntlmssp_negotiate.dat", request.data, request.length);
|
---|
135 | #endif
|
---|
136 |
|
---|
137 | if (in.length) {
|
---|
138 | if ((in.length < 16) || !msrpc_parse(out_mem_ctx,
|
---|
139 | &in, "Cdd",
|
---|
140 | "NTLMSSP",
|
---|
141 | &ntlmssp_command,
|
---|
142 | &neg_flags)) {
|
---|
143 | DEBUG(1, ("ntlmssp_server_negotiate: failed to parse "
|
---|
144 | "NTLMSSP Negotiate of length %u:\n",
|
---|
145 | (unsigned int)in.length ));
|
---|
146 | dump_data(2, in.data, in.length);
|
---|
147 | return NT_STATUS_INVALID_PARAMETER;
|
---|
148 | }
|
---|
149 | debug_ntlmssp_flags(neg_flags);
|
---|
150 | }
|
---|
151 |
|
---|
152 | ntlmssp_handle_neg_flags(gensec_ntlmssp_state, neg_flags, gensec_ntlmssp_state->allow_lm_key);
|
---|
153 |
|
---|
154 | /* Ask our caller what challenge they would like in the packet */
|
---|
155 | cryptkey = gensec_ntlmssp_state->get_challenge(gensec_ntlmssp_state);
|
---|
156 | if (!cryptkey) {
|
---|
157 | DEBUG(1, ("ntlmssp_server_negotiate: backend doesn't give a challenge\n"));
|
---|
158 | return NT_STATUS_INTERNAL_ERROR;
|
---|
159 | }
|
---|
160 |
|
---|
161 | /* Check if we may set the challenge */
|
---|
162 | if (!gensec_ntlmssp_state->may_set_challenge(gensec_ntlmssp_state)) {
|
---|
163 | gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
|
---|
164 | }
|
---|
165 |
|
---|
166 | /* The flags we send back are not just the negotiated flags,
|
---|
167 | * they are also 'what is in this packet'. Therfore, we
|
---|
168 | * operate on 'chal_flags' from here on
|
---|
169 | */
|
---|
170 |
|
---|
171 | chal_flags = gensec_ntlmssp_state->neg_flags;
|
---|
172 |
|
---|
173 | /* get the right name to fill in as 'target' */
|
---|
174 | target_name = ntlmssp_target_name(gensec_ntlmssp_state,
|
---|
175 | neg_flags, &chal_flags);
|
---|
176 | if (target_name == NULL)
|
---|
177 | return NT_STATUS_INVALID_PARAMETER;
|
---|
178 |
|
---|
179 | gensec_ntlmssp_state->chal = data_blob_talloc(gensec_ntlmssp_state, cryptkey, 8);
|
---|
180 | gensec_ntlmssp_state->internal_chal = data_blob_talloc(gensec_ntlmssp_state, cryptkey, 8);
|
---|
181 |
|
---|
182 | /* This creates the 'blob' of names that appears at the end of the packet */
|
---|
183 | if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) {
|
---|
184 | char dnsdomname[MAXHOSTNAMELEN], dnsname[MAXHOSTNAMELEN];
|
---|
185 | const char *target_name_dns = "";
|
---|
186 |
|
---|
187 | /* Find out the DNS domain name */
|
---|
188 | dnsdomname[0] = '\0';
|
---|
189 | safe_strcpy(dnsdomname, lp_realm(gensec_security->settings->lp_ctx), sizeof(dnsdomname) - 1);
|
---|
190 | strlower_m(dnsdomname);
|
---|
191 |
|
---|
192 | /* Find out the DNS host name */
|
---|
193 | safe_strcpy(dnsname, gensec_ntlmssp_state->server_name, sizeof(dnsname) - 1);
|
---|
194 | if (dnsdomname[0] != '\0') {
|
---|
195 | safe_strcat(dnsname, ".", sizeof(dnsname) - 1);
|
---|
196 | safe_strcat(dnsname, dnsdomname, sizeof(dnsname) - 1);
|
---|
197 | }
|
---|
198 | strlower_m(dnsname);
|
---|
199 |
|
---|
200 | if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) {
|
---|
201 | target_name_dns = dnsdomname;
|
---|
202 | } else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) {
|
---|
203 | target_name_dns = dnsname;
|
---|
204 | }
|
---|
205 |
|
---|
206 | msrpc_gen(out_mem_ctx,
|
---|
207 | &struct_blob, "aaaaa",
|
---|
208 | MsvAvNbDomainName, target_name,
|
---|
209 | MsvAvNbComputerName, gensec_ntlmssp_state->server_name,
|
---|
210 | MsvAvDnsDomainName, dnsdomname,
|
---|
211 | MsvAvDnsComputerName, dnsname,
|
---|
212 | MsvAvEOL, "");
|
---|
213 | } else {
|
---|
214 | struct_blob = data_blob(NULL, 0);
|
---|
215 | }
|
---|
216 |
|
---|
217 | {
|
---|
218 | /* Marshal the packet in the right format, be it unicode or ASCII */
|
---|
219 | const char *gen_string;
|
---|
220 | if (gensec_ntlmssp_state->unicode) {
|
---|
221 | gen_string = "CdUdbddB";
|
---|
222 | } else {
|
---|
223 | gen_string = "CdAdbddB";
|
---|
224 | }
|
---|
225 |
|
---|
226 | msrpc_gen(out_mem_ctx,
|
---|
227 | out, gen_string,
|
---|
228 | "NTLMSSP",
|
---|
229 | NTLMSSP_CHALLENGE,
|
---|
230 | target_name,
|
---|
231 | chal_flags,
|
---|
232 | cryptkey, 8,
|
---|
233 | 0, 0,
|
---|
234 | struct_blob.data, struct_blob.length);
|
---|
235 | }
|
---|
236 |
|
---|
237 | gensec_ntlmssp_state->expected_state = NTLMSSP_AUTH;
|
---|
238 |
|
---|
239 | return NT_STATUS_MORE_PROCESSING_REQUIRED;
|
---|
240 | }
|
---|
241 |
|
---|
242 | /**
|
---|
243 | * Next state function for the Authenticate packet
|
---|
244 | *
|
---|
245 | * @param gensec_ntlmssp_state NTLMSSP State
|
---|
246 | * @param request The request, as a DATA_BLOB
|
---|
247 | * @return Errors or NT_STATUS_OK.
|
---|
248 | */
|
---|
249 |
|
---|
250 | static NTSTATUS ntlmssp_server_preauth(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
---|
251 | const DATA_BLOB request)
|
---|
252 | {
|
---|
253 | uint32_t ntlmssp_command, auth_flags;
|
---|
254 | NTSTATUS nt_status;
|
---|
255 |
|
---|
256 | uint8_t session_nonce_hash[16];
|
---|
257 |
|
---|
258 | const char *parse_string;
|
---|
259 | char *domain = NULL;
|
---|
260 | char *user = NULL;
|
---|
261 | char *workstation = NULL;
|
---|
262 |
|
---|
263 | #if 0
|
---|
264 | file_save("ntlmssp_auth.dat", request.data, request.length);
|
---|
265 | #endif
|
---|
266 |
|
---|
267 | if (gensec_ntlmssp_state->unicode) {
|
---|
268 | parse_string = "CdBBUUUBd";
|
---|
269 | } else {
|
---|
270 | parse_string = "CdBBAAABd";
|
---|
271 | }
|
---|
272 |
|
---|
273 | /* zero these out */
|
---|
274 | data_blob_free(&gensec_ntlmssp_state->lm_resp);
|
---|
275 | data_blob_free(&gensec_ntlmssp_state->nt_resp);
|
---|
276 | data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
---|
277 |
|
---|
278 | gensec_ntlmssp_state->user = NULL;
|
---|
279 | gensec_ntlmssp_state->domain = NULL;
|
---|
280 | gensec_ntlmssp_state->workstation = NULL;
|
---|
281 |
|
---|
282 | /* now the NTLMSSP encoded auth hashes */
|
---|
283 | if (!msrpc_parse(gensec_ntlmssp_state,
|
---|
284 | &request, parse_string,
|
---|
285 | "NTLMSSP",
|
---|
286 | &ntlmssp_command,
|
---|
287 | &gensec_ntlmssp_state->lm_resp,
|
---|
288 | &gensec_ntlmssp_state->nt_resp,
|
---|
289 | &domain,
|
---|
290 | &user,
|
---|
291 | &workstation,
|
---|
292 | &gensec_ntlmssp_state->encrypted_session_key,
|
---|
293 | &auth_flags)) {
|
---|
294 | DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n"));
|
---|
295 | dump_data(10, request.data, request.length);
|
---|
296 |
|
---|
297 | /* zero this out */
|
---|
298 | data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
---|
299 | auth_flags = 0;
|
---|
300 |
|
---|
301 | /* Try again with a shorter string (Win9X truncates this packet) */
|
---|
302 | if (gensec_ntlmssp_state->unicode) {
|
---|
303 | parse_string = "CdBBUUU";
|
---|
304 | } else {
|
---|
305 | parse_string = "CdBBAAA";
|
---|
306 | }
|
---|
307 |
|
---|
308 | /* now the NTLMSSP encoded auth hashes */
|
---|
309 | if (!msrpc_parse(gensec_ntlmssp_state,
|
---|
310 | &request, parse_string,
|
---|
311 | "NTLMSSP",
|
---|
312 | &ntlmssp_command,
|
---|
313 | &gensec_ntlmssp_state->lm_resp,
|
---|
314 | &gensec_ntlmssp_state->nt_resp,
|
---|
315 | &domain,
|
---|
316 | &user,
|
---|
317 | &workstation)) {
|
---|
318 | DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
|
---|
319 | dump_data(2, request.data, request.length);
|
---|
320 |
|
---|
321 | return NT_STATUS_INVALID_PARAMETER;
|
---|
322 | }
|
---|
323 | }
|
---|
324 |
|
---|
325 | if (auth_flags)
|
---|
326 | ntlmssp_handle_neg_flags(gensec_ntlmssp_state, auth_flags, gensec_ntlmssp_state->allow_lm_key);
|
---|
327 |
|
---|
328 | if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(gensec_ntlmssp_state, domain))) {
|
---|
329 | /* zero this out */
|
---|
330 | data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
---|
331 | return nt_status;
|
---|
332 | }
|
---|
333 |
|
---|
334 | if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(gensec_ntlmssp_state, user))) {
|
---|
335 | /* zero this out */
|
---|
336 | data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
---|
337 | return nt_status;
|
---|
338 | }
|
---|
339 |
|
---|
340 | if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(gensec_ntlmssp_state, workstation))) {
|
---|
341 | /* zero this out */
|
---|
342 | data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
---|
343 | return nt_status;
|
---|
344 | }
|
---|
345 |
|
---|
346 | DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n",
|
---|
347 | gensec_ntlmssp_state->user, gensec_ntlmssp_state->domain, gensec_ntlmssp_state->workstation, (unsigned long)gensec_ntlmssp_state->lm_resp.length, (unsigned long)gensec_ntlmssp_state->nt_resp.length));
|
---|
348 |
|
---|
349 | #if 0
|
---|
350 | file_save("nthash1.dat", &gensec_ntlmssp_state->nt_resp.data, &gensec_ntlmssp_state->nt_resp.length);
|
---|
351 | file_save("lmhash1.dat", &gensec_ntlmssp_state->lm_resp.data, &gensec_ntlmssp_state->lm_resp.length);
|
---|
352 | #endif
|
---|
353 |
|
---|
354 | /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a
|
---|
355 | client challenge
|
---|
356 |
|
---|
357 | However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful.
|
---|
358 | */
|
---|
359 | if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
---|
360 | if (gensec_ntlmssp_state->nt_resp.length == 24 && gensec_ntlmssp_state->lm_resp.length == 24) {
|
---|
361 | struct MD5Context md5_session_nonce_ctx;
|
---|
362 | SMB_ASSERT(gensec_ntlmssp_state->internal_chal.data
|
---|
363 | && gensec_ntlmssp_state->internal_chal.length == 8);
|
---|
364 |
|
---|
365 | gensec_ntlmssp_state->doing_ntlm2 = true;
|
---|
366 |
|
---|
367 | memcpy(gensec_ntlmssp_state->crypt.ntlm2.session_nonce, gensec_ntlmssp_state->internal_chal.data, 8);
|
---|
368 | memcpy(&gensec_ntlmssp_state->crypt.ntlm2.session_nonce[8], gensec_ntlmssp_state->lm_resp.data, 8);
|
---|
369 |
|
---|
370 | MD5Init(&md5_session_nonce_ctx);
|
---|
371 | MD5Update(&md5_session_nonce_ctx, gensec_ntlmssp_state->crypt.ntlm2.session_nonce, 16);
|
---|
372 | MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
|
---|
373 |
|
---|
374 | gensec_ntlmssp_state->chal = data_blob_talloc(gensec_ntlmssp_state,
|
---|
375 | session_nonce_hash, 8);
|
---|
376 |
|
---|
377 | /* LM response is no longer useful, zero it out */
|
---|
378 | data_blob_free(&gensec_ntlmssp_state->lm_resp);
|
---|
379 |
|
---|
380 | /* We changed the effective challenge - set it */
|
---|
381 | if (!NT_STATUS_IS_OK(nt_status =
|
---|
382 | gensec_ntlmssp_state->set_challenge(gensec_ntlmssp_state,
|
---|
383 | &gensec_ntlmssp_state->chal))) {
|
---|
384 | /* zero this out */
|
---|
385 | data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
---|
386 | return nt_status;
|
---|
387 | }
|
---|
388 |
|
---|
389 | /* LM Key is incompatible... */
|
---|
390 | gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
---|
391 | }
|
---|
392 | }
|
---|
393 | return NT_STATUS_OK;
|
---|
394 | }
|
---|
395 |
|
---|
396 | /**
|
---|
397 | * Next state function for the Authenticate packet
|
---|
398 | * (after authentication - figures out the session keys etc)
|
---|
399 | *
|
---|
400 | * @param gensec_ntlmssp_state NTLMSSP State
|
---|
401 | * @return Errors or NT_STATUS_OK.
|
---|
402 | */
|
---|
403 |
|
---|
404 | static NTSTATUS ntlmssp_server_postauth(struct gensec_security *gensec_security,
|
---|
405 | DATA_BLOB *user_session_key,
|
---|
406 | DATA_BLOB *lm_session_key)
|
---|
407 | {
|
---|
408 | struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
|
---|
409 | NTSTATUS nt_status;
|
---|
410 | DATA_BLOB session_key = data_blob(NULL, 0);
|
---|
411 |
|
---|
412 | if (user_session_key)
|
---|
413 | dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length);
|
---|
414 |
|
---|
415 | if (lm_session_key)
|
---|
416 | dump_data_pw("LM first-8:\n", lm_session_key->data, lm_session_key->length);
|
---|
417 |
|
---|
418 | /* Handle the different session key derivation for NTLM2 */
|
---|
419 | if (gensec_ntlmssp_state->doing_ntlm2) {
|
---|
420 | if (user_session_key && user_session_key->data && user_session_key->length == 16) {
|
---|
421 | session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
|
---|
422 | hmac_md5(user_session_key->data, gensec_ntlmssp_state->crypt.ntlm2.session_nonce,
|
---|
423 | sizeof(gensec_ntlmssp_state->crypt.ntlm2.session_nonce), session_key.data);
|
---|
424 | DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n"));
|
---|
425 | dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
|
---|
426 |
|
---|
427 | } else {
|
---|
428 | DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n"));
|
---|
429 | session_key = data_blob(NULL, 0);
|
---|
430 | }
|
---|
431 | } else if ((gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
|
---|
432 | /* Ensure we can never get here on NTLMv2 */
|
---|
433 | && (gensec_ntlmssp_state->nt_resp.length == 0 || gensec_ntlmssp_state->nt_resp.length == 24)) {
|
---|
434 |
|
---|
435 | if (lm_session_key && lm_session_key->data && lm_session_key->length >= 8) {
|
---|
436 | if (gensec_ntlmssp_state->lm_resp.data && gensec_ntlmssp_state->lm_resp.length == 24) {
|
---|
437 | session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
|
---|
438 | SMBsesskeygen_lm_sess_key(lm_session_key->data, gensec_ntlmssp_state->lm_resp.data,
|
---|
439 | session_key.data);
|
---|
440 | DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
|
---|
441 | dump_data_pw("LM session key:\n", session_key.data, session_key.length);
|
---|
442 | } else {
|
---|
443 |
|
---|
444 | /* When there is no LM response, just use zeros */
|
---|
445 | static const uint8_t zeros[24];
|
---|
446 | session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
|
---|
447 | SMBsesskeygen_lm_sess_key(zeros, zeros,
|
---|
448 | session_key.data);
|
---|
449 | DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
|
---|
450 | dump_data_pw("LM session key:\n", session_key.data, session_key.length);
|
---|
451 | }
|
---|
452 | } else {
|
---|
453 | /* LM Key not selected */
|
---|
454 | gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
---|
455 |
|
---|
456 | DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n"));
|
---|
457 | session_key = data_blob(NULL, 0);
|
---|
458 | }
|
---|
459 |
|
---|
460 | } else if (user_session_key && user_session_key->data) {
|
---|
461 | session_key = data_blob_talloc(gensec_ntlmssp_state, user_session_key->data, user_session_key->length);
|
---|
462 | DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n"));
|
---|
463 | dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
|
---|
464 |
|
---|
465 | /* LM Key not selected */
|
---|
466 | gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
---|
467 |
|
---|
468 | } else if (lm_session_key && lm_session_key->data) {
|
---|
469 | /* Very weird to have LM key, but no user session key, but anyway.. */
|
---|
470 | session_key = data_blob_talloc(gensec_ntlmssp_state, lm_session_key->data, lm_session_key->length);
|
---|
471 | DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n"));
|
---|
472 | dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
|
---|
473 |
|
---|
474 | /* LM Key not selected */
|
---|
475 | gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
---|
476 |
|
---|
477 | } else {
|
---|
478 | DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n"));
|
---|
479 | session_key = data_blob(NULL, 0);
|
---|
480 |
|
---|
481 | /* LM Key not selected */
|
---|
482 | gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
---|
483 | }
|
---|
484 |
|
---|
485 | /* With KEY_EXCH, the client supplies the proposed session key,
|
---|
486 | but encrypts it with the long-term key */
|
---|
487 | if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
|
---|
488 | if (!gensec_ntlmssp_state->encrypted_session_key.data
|
---|
489 | || gensec_ntlmssp_state->encrypted_session_key.length != 16) {
|
---|
490 | data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
---|
491 | DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n",
|
---|
492 | (unsigned)gensec_ntlmssp_state->encrypted_session_key.length));
|
---|
493 | return NT_STATUS_INVALID_PARAMETER;
|
---|
494 | } else if (!session_key.data || session_key.length != 16) {
|
---|
495 | DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n",
|
---|
496 | (unsigned)session_key.length));
|
---|
497 | gensec_ntlmssp_state->session_key = session_key;
|
---|
498 | } else {
|
---|
499 | dump_data_pw("KEY_EXCH session key (enc):\n",
|
---|
500 | gensec_ntlmssp_state->encrypted_session_key.data,
|
---|
501 | gensec_ntlmssp_state->encrypted_session_key.length);
|
---|
502 | arcfour_crypt(gensec_ntlmssp_state->encrypted_session_key.data,
|
---|
503 | session_key.data,
|
---|
504 | gensec_ntlmssp_state->encrypted_session_key.length);
|
---|
505 | gensec_ntlmssp_state->session_key = data_blob_talloc(gensec_ntlmssp_state,
|
---|
506 | gensec_ntlmssp_state->encrypted_session_key.data,
|
---|
507 | gensec_ntlmssp_state->encrypted_session_key.length);
|
---|
508 | dump_data_pw("KEY_EXCH session key:\n", gensec_ntlmssp_state->encrypted_session_key.data,
|
---|
509 | gensec_ntlmssp_state->encrypted_session_key.length);
|
---|
510 | talloc_free(session_key.data);
|
---|
511 | }
|
---|
512 | } else {
|
---|
513 | gensec_ntlmssp_state->session_key = session_key;
|
---|
514 | }
|
---|
515 |
|
---|
516 | if ((gensec_security->want_features & GENSEC_FEATURE_SIGN)
|
---|
517 | || (gensec_security->want_features & GENSEC_FEATURE_SEAL)) {
|
---|
518 | nt_status = ntlmssp_sign_init(gensec_ntlmssp_state);
|
---|
519 | } else {
|
---|
520 | nt_status = NT_STATUS_OK;
|
---|
521 | }
|
---|
522 |
|
---|
523 | data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
---|
524 |
|
---|
525 | /* allow arbitarily many authentications, but watch that this will cause a
|
---|
526 | memory leak, until the gensec_ntlmssp_state is shutdown
|
---|
527 | */
|
---|
528 |
|
---|
529 | if (gensec_ntlmssp_state->server_multiple_authentications) {
|
---|
530 | gensec_ntlmssp_state->expected_state = NTLMSSP_AUTH;
|
---|
531 | } else {
|
---|
532 | gensec_ntlmssp_state->expected_state = NTLMSSP_DONE;
|
---|
533 | }
|
---|
534 |
|
---|
535 | return nt_status;
|
---|
536 | }
|
---|
537 |
|
---|
538 |
|
---|
539 | /**
|
---|
540 | * Next state function for the Authenticate packet
|
---|
541 | *
|
---|
542 | * @param gensec_security GENSEC state
|
---|
543 | * @param out_mem_ctx Memory context for *out
|
---|
544 | * @param in The request, as a DATA_BLOB. reply.data must be NULL
|
---|
545 | * @param out The reply, as an allocated DATA_BLOB, caller to free.
|
---|
546 | * @return Errors or NT_STATUS_OK if authentication sucessful
|
---|
547 | */
|
---|
548 |
|
---|
549 | NTSTATUS ntlmssp_server_auth(struct gensec_security *gensec_security,
|
---|
550 | TALLOC_CTX *out_mem_ctx,
|
---|
551 | const DATA_BLOB in, DATA_BLOB *out)
|
---|
552 | {
|
---|
553 | struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
|
---|
554 | DATA_BLOB user_session_key = data_blob(NULL, 0);
|
---|
555 | DATA_BLOB lm_session_key = data_blob(NULL, 0);
|
---|
556 | NTSTATUS nt_status;
|
---|
557 |
|
---|
558 | TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
|
---|
559 | if (!mem_ctx) {
|
---|
560 | return NT_STATUS_NO_MEMORY;
|
---|
561 | }
|
---|
562 |
|
---|
563 | /* zero the outbound NTLMSSP packet */
|
---|
564 | *out = data_blob_talloc(out_mem_ctx, NULL, 0);
|
---|
565 |
|
---|
566 | if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(gensec_ntlmssp_state, in))) {
|
---|
567 | talloc_free(mem_ctx);
|
---|
568 | return nt_status;
|
---|
569 | }
|
---|
570 |
|
---|
571 | /*
|
---|
572 | * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth
|
---|
573 | * is required (by "ntlm auth = no" and "lm auth = no" being set in the
|
---|
574 | * smb.conf file) and no NTLMv2 response was sent then the password check
|
---|
575 | * will fail here. JRA.
|
---|
576 | */
|
---|
577 |
|
---|
578 | /* Finally, actually ask if the password is OK */
|
---|
579 |
|
---|
580 | if (!NT_STATUS_IS_OK(nt_status = gensec_ntlmssp_state->check_password(gensec_ntlmssp_state, mem_ctx,
|
---|
581 | &user_session_key, &lm_session_key))) {
|
---|
582 | talloc_free(mem_ctx);
|
---|
583 | return nt_status;
|
---|
584 | }
|
---|
585 |
|
---|
586 | if (gensec_security->want_features
|
---|
587 | & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL|GENSEC_FEATURE_SESSION_KEY)) {
|
---|
588 | nt_status = ntlmssp_server_postauth(gensec_security, &user_session_key, &lm_session_key);
|
---|
589 | talloc_free(mem_ctx);
|
---|
590 | return nt_status;
|
---|
591 | } else {
|
---|
592 | gensec_ntlmssp_state->session_key = data_blob(NULL, 0);
|
---|
593 | talloc_free(mem_ctx);
|
---|
594 | return NT_STATUS_OK;
|
---|
595 | }
|
---|
596 | }
|
---|
597 |
|
---|
598 | /**
|
---|
599 | * Return the challenge as determined by the authentication subsystem
|
---|
600 | * @return an 8 byte random challenge
|
---|
601 | */
|
---|
602 |
|
---|
603 | static const uint8_t *auth_ntlmssp_get_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
|
---|
604 | {
|
---|
605 | NTSTATUS status;
|
---|
606 | const uint8_t *chal;
|
---|
607 |
|
---|
608 | status = gensec_ntlmssp_state->auth_context->get_challenge(gensec_ntlmssp_state->auth_context, &chal);
|
---|
609 | if (!NT_STATUS_IS_OK(status)) {
|
---|
610 | DEBUG(1, ("auth_ntlmssp_get_challenge: failed to get challenge: %s\n",
|
---|
611 | nt_errstr(status)));
|
---|
612 | return NULL;
|
---|
613 | }
|
---|
614 |
|
---|
615 | return chal;
|
---|
616 | }
|
---|
617 |
|
---|
618 | /**
|
---|
619 | * Some authentication methods 'fix' the challenge, so we may not be able to set it
|
---|
620 | *
|
---|
621 | * @return If the effective challenge used by the auth subsystem may be modified
|
---|
622 | */
|
---|
623 | static bool auth_ntlmssp_may_set_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
|
---|
624 | {
|
---|
625 | return gensec_ntlmssp_state->auth_context->challenge_may_be_modified(gensec_ntlmssp_state->auth_context);
|
---|
626 | }
|
---|
627 |
|
---|
628 | /**
|
---|
629 | * NTLM2 authentication modifies the effective challenge,
|
---|
630 | * @param challenge The new challenge value
|
---|
631 | */
|
---|
632 | static NTSTATUS auth_ntlmssp_set_challenge(struct gensec_ntlmssp_state *gensec_ntlmssp_state, DATA_BLOB *challenge)
|
---|
633 | {
|
---|
634 | NTSTATUS nt_status;
|
---|
635 | struct auth_context *auth_context = gensec_ntlmssp_state->auth_context;
|
---|
636 | const uint8_t *chal;
|
---|
637 |
|
---|
638 | if (challenge->length != 8) {
|
---|
639 | return NT_STATUS_INVALID_PARAMETER;
|
---|
640 | }
|
---|
641 |
|
---|
642 | chal = challenge->data;
|
---|
643 |
|
---|
644 | nt_status = gensec_ntlmssp_state->auth_context->set_challenge(auth_context,
|
---|
645 | chal,
|
---|
646 | "NTLMSSP callback (NTLM2)");
|
---|
647 |
|
---|
648 | return nt_status;
|
---|
649 | }
|
---|
650 |
|
---|
651 | /**
|
---|
652 | * Check the password on an NTLMSSP login.
|
---|
653 | *
|
---|
654 | * Return the session keys used on the connection.
|
---|
655 | */
|
---|
656 |
|
---|
657 | static NTSTATUS auth_ntlmssp_check_password(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
---|
658 | TALLOC_CTX *mem_ctx,
|
---|
659 | DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
|
---|
660 | {
|
---|
661 | NTSTATUS nt_status;
|
---|
662 | struct auth_usersupplied_info *user_info = talloc(mem_ctx, struct auth_usersupplied_info);
|
---|
663 | if (!user_info) {
|
---|
664 | return NT_STATUS_NO_MEMORY;
|
---|
665 | }
|
---|
666 |
|
---|
667 | user_info->logon_parameters = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
|
---|
668 | user_info->flags = 0;
|
---|
669 | user_info->mapped_state = false;
|
---|
670 | user_info->client.account_name = gensec_ntlmssp_state->user;
|
---|
671 | user_info->client.domain_name = gensec_ntlmssp_state->domain;
|
---|
672 | user_info->workstation_name = gensec_ntlmssp_state->workstation;
|
---|
673 | user_info->remote_host = gensec_get_peer_addr(gensec_ntlmssp_state->gensec_security);
|
---|
674 |
|
---|
675 | user_info->password_state = AUTH_PASSWORD_RESPONSE;
|
---|
676 | user_info->password.response.lanman = gensec_ntlmssp_state->lm_resp;
|
---|
677 | user_info->password.response.lanman.data = talloc_steal(user_info, gensec_ntlmssp_state->lm_resp.data);
|
---|
678 | user_info->password.response.nt = gensec_ntlmssp_state->nt_resp;
|
---|
679 | user_info->password.response.nt.data = talloc_steal(user_info, gensec_ntlmssp_state->nt_resp.data);
|
---|
680 |
|
---|
681 | nt_status = gensec_ntlmssp_state->auth_context->check_password(gensec_ntlmssp_state->auth_context,
|
---|
682 | mem_ctx,
|
---|
683 | user_info,
|
---|
684 | &gensec_ntlmssp_state->server_info);
|
---|
685 | talloc_free(user_info);
|
---|
686 | NT_STATUS_NOT_OK_RETURN(nt_status);
|
---|
687 |
|
---|
688 | talloc_steal(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info);
|
---|
689 |
|
---|
690 | if (gensec_ntlmssp_state->server_info->user_session_key.length) {
|
---|
691 | DEBUG(10, ("Got NT session key of length %u\n",
|
---|
692 | (unsigned)gensec_ntlmssp_state->server_info->user_session_key.length));
|
---|
693 | if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->user_session_key.data)) {
|
---|
694 | return NT_STATUS_NO_MEMORY;
|
---|
695 | }
|
---|
696 |
|
---|
697 | *user_session_key = gensec_ntlmssp_state->server_info->user_session_key;
|
---|
698 | }
|
---|
699 | if (gensec_ntlmssp_state->server_info->lm_session_key.length) {
|
---|
700 | DEBUG(10, ("Got LM session key of length %u\n",
|
---|
701 | (unsigned)gensec_ntlmssp_state->server_info->lm_session_key.length));
|
---|
702 | if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->lm_session_key.data)) {
|
---|
703 | return NT_STATUS_NO_MEMORY;
|
---|
704 | }
|
---|
705 |
|
---|
706 | *lm_session_key = gensec_ntlmssp_state->server_info->lm_session_key;
|
---|
707 | }
|
---|
708 | return nt_status;
|
---|
709 | }
|
---|
710 |
|
---|
711 | /**
|
---|
712 | * Return the credentials of a logged on user, including session keys
|
---|
713 | * etc.
|
---|
714 | *
|
---|
715 | * Only valid after a successful authentication
|
---|
716 | *
|
---|
717 | * May only be called once per authentication.
|
---|
718 | *
|
---|
719 | */
|
---|
720 |
|
---|
721 | NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_security,
|
---|
722 | struct auth_session_info **session_info)
|
---|
723 | {
|
---|
724 | NTSTATUS nt_status;
|
---|
725 | struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
|
---|
726 |
|
---|
727 | nt_status = auth_generate_session_info(gensec_ntlmssp_state, gensec_security->event_ctx, gensec_security->settings->lp_ctx, gensec_ntlmssp_state->server_info, session_info);
|
---|
728 | NT_STATUS_NOT_OK_RETURN(nt_status);
|
---|
729 |
|
---|
730 | (*session_info)->session_key = data_blob_talloc(*session_info,
|
---|
731 | gensec_ntlmssp_state->session_key.data,
|
---|
732 | gensec_ntlmssp_state->session_key.length);
|
---|
733 |
|
---|
734 | return NT_STATUS_OK;
|
---|
735 | }
|
---|
736 |
|
---|
737 | /**
|
---|
738 | * Start NTLMSSP on the server side
|
---|
739 | *
|
---|
740 | */
|
---|
741 | NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_security)
|
---|
742 | {
|
---|
743 | NTSTATUS nt_status;
|
---|
744 | struct gensec_ntlmssp_state *gensec_ntlmssp_state;
|
---|
745 |
|
---|
746 | nt_status = gensec_ntlmssp_start(gensec_security);
|
---|
747 | NT_STATUS_NOT_OK_RETURN(nt_status);
|
---|
748 |
|
---|
749 | gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
|
---|
750 |
|
---|
751 | gensec_ntlmssp_state->role = NTLMSSP_SERVER;
|
---|
752 |
|
---|
753 | gensec_ntlmssp_state->workstation = NULL;
|
---|
754 | gensec_ntlmssp_state->server_name = lp_netbios_name(gensec_security->settings->lp_ctx);
|
---|
755 |
|
---|
756 | gensec_ntlmssp_state->domain = lp_workgroup(gensec_security->settings->lp_ctx);
|
---|
757 |
|
---|
758 | gensec_ntlmssp_state->expected_state = NTLMSSP_NEGOTIATE;
|
---|
759 |
|
---|
760 | gensec_ntlmssp_state->allow_lm_key = (lp_lanman_auth(gensec_security->settings->lp_ctx)
|
---|
761 | && gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "allow_lm_key", false));
|
---|
762 |
|
---|
763 | gensec_ntlmssp_state->server_multiple_authentications = false;
|
---|
764 |
|
---|
765 | gensec_ntlmssp_state->neg_flags =
|
---|
766 | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_VERSION;
|
---|
767 |
|
---|
768 | gensec_ntlmssp_state->lm_resp = data_blob(NULL, 0);
|
---|
769 | gensec_ntlmssp_state->nt_resp = data_blob(NULL, 0);
|
---|
770 | gensec_ntlmssp_state->encrypted_session_key = data_blob(NULL, 0);
|
---|
771 |
|
---|
772 | if (gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "128bit", true)) {
|
---|
773 | gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_128;
|
---|
774 | }
|
---|
775 |
|
---|
776 | if (gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "56bit", true)) {
|
---|
777 | gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56;
|
---|
778 | }
|
---|
779 |
|
---|
780 | if (gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "keyexchange", true)) {
|
---|
781 | gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
|
---|
782 | }
|
---|
783 |
|
---|
784 | if (gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "alwayssign", true)) {
|
---|
785 | gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
|
---|
786 | }
|
---|
787 |
|
---|
788 | if (gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "ntlm2", true)) {
|
---|
789 | gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
|
---|
790 | }
|
---|
791 |
|
---|
792 | if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
|
---|
793 | gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
|
---|
794 | }
|
---|
795 | if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
|
---|
796 | gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
|
---|
797 | }
|
---|
798 |
|
---|
799 | gensec_ntlmssp_state->auth_context = gensec_security->auth_context;
|
---|
800 |
|
---|
801 | gensec_ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge;
|
---|
802 | gensec_ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge;
|
---|
803 | gensec_ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge;
|
---|
804 | gensec_ntlmssp_state->check_password = auth_ntlmssp_check_password;
|
---|
805 | gensec_ntlmssp_state->server_role = lp_server_role(gensec_security->settings->lp_ctx);
|
---|
806 |
|
---|
807 | return NT_STATUS_OK;
|
---|
808 | }
|
---|
809 |
|
---|