r4777: added a smb_composite_sesssetup() async composite function. This
[gd/samba-autobuild/.git] / source4 / libcli / composite / sesssetup.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Andrew Tridgell 2005
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 /*
21   a composite API for making handling a generic async session setup
22 */
23
24 #include "includes.h"
25 #include "libcli/raw/libcliraw.h"
26 #include "libcli/composite/composite.h"
27 #include "auth/auth.h"
28
29
30 struct sesssetup_state {
31         union smb_sesssetup setup;
32         NTSTATUS session_key_err;
33 };
34
35
36 /*
37   form an encrypted lanman password from a plaintext password
38   and the server supplied challenge
39 */
40 static DATA_BLOB lanman_blob(TALLOC_CTX *mem_ctx, const char *pass, DATA_BLOB challenge)
41 {
42         DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
43         SMBencrypt(pass, challenge.data, blob.data);
44         return blob;
45 }
46
47 /*
48   form an encrypted NT password from a plaintext password
49   and the server supplied challenge
50 */
51 static DATA_BLOB nt_blob(TALLOC_CTX *mem_ctx, const char *pass, DATA_BLOB challenge)
52 {
53         DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
54         SMBNTencrypt(pass, challenge.data, blob.data);
55         return blob;
56 }
57
58 /*
59   store the user session key for a transport
60 */
61 static void set_user_session_key(struct smbcli_session *session,
62                                  const DATA_BLOB *session_key)
63 {
64         session->user_session_key = data_blob_talloc(session, 
65                                                      session_key->data, 
66                                                      session_key->length);
67 }
68
69 /*
70   setup signing for a NT1 style session setup
71 */
72 static void use_nt1_session_keys(struct smbcli_session *session, 
73                                  const char *password, const DATA_BLOB *nt_response)
74 {
75         struct smbcli_transport *transport = session->transport; 
76         uint8_t nt_hash[16];
77         DATA_BLOB session_key = data_blob_talloc(session, NULL, 16);
78
79         E_md4hash(password, nt_hash);
80         SMBsesskeygen_ntv1(nt_hash, session_key.data);
81
82         smbcli_transport_simple_set_signing(transport, session_key, *nt_response);
83
84         set_user_session_key(session, &session_key);
85         data_blob_free(&session_key);
86 }
87
88
89 /*
90   handler for completion of a smbcli_request sub-request
91 */
92 static void request_handler(struct smbcli_request *req)
93 {
94         struct smbcli_composite *c = req->async.private;
95         struct sesssetup_state *state = c->private;
96         struct smb_composite_sesssetup *io = c->composite_parms;
97         struct smbcli_session *session = req->session;
98         DATA_BLOB session_key = data_blob(NULL, 0);
99         DATA_BLOB null_data_blob = data_blob(NULL, 0);
100
101         c->status = smb_raw_session_setup_recv(req, state, &state->setup);
102
103         switch (state->setup.old.level) {
104         case RAW_SESSSETUP_OLD:
105                 io->out.vuid = state->setup.old.out.vuid;
106                 break;
107
108         case RAW_SESSSETUP_NT1:
109                 io->out.vuid = state->setup.nt1.out.vuid;
110                 break;
111
112         case RAW_SESSSETUP_SPNEGO:
113                 session->vuid = io->out.vuid = state->setup.spnego.out.vuid;
114                 if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && 
115                     !NT_STATUS_IS_OK(c->status)) {
116                         break;
117                 }
118                 c->status = gensec_update(session->gensec, state,
119                                           state->setup.spnego.out.secblob,
120                                           &state->setup.spnego.in.secblob);
121                 if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && 
122                     !NT_STATUS_IS_OK(c->status)) {
123                         break;
124                 }
125                 if (state->setup.spnego.in.secblob.length == 0) {
126                         break;
127                 }
128
129                 /* we need to do another round of session setup. We keep going until both sides
130                    are happy */
131                 state->session_key_err = gensec_session_key(session->gensec, &session_key);
132                 if (NT_STATUS_IS_OK(state->session_key_err)) {
133                         smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
134                 }
135
136                 req = smb_raw_session_setup_send(session, &state->setup);
137                 req->async.fn = request_handler;
138                 req->async.private = c;
139                 c->req = req;
140                 return;
141         }
142
143         /* enforce the local signing required flag */
144         if (NT_STATUS_IS_OK(c->status) && io->in.user && io->in.user[0]) {
145                 if (!session->transport->negotiate.sign_info.doing_signing 
146                     && session->transport->negotiate.sign_info.mandatory_signing) {
147                         DEBUG(0, ("SMB signing required, but server does not support it\n"));
148                         c->status = NT_STATUS_ACCESS_DENIED;
149                 }
150         }
151
152         if (NT_STATUS_IS_OK(c->status)) {
153                 c->state = SMBCLI_REQUEST_DONE;
154         } else {
155                 c->state = SMBCLI_REQUEST_ERROR;
156         }
157         if (c->async.fn) {
158                 c->async.fn(c);
159         }
160 }
161
162
163 /*
164   send a nt1 style session setup
165 */
166 static struct smbcli_request *session_setup_nt1(struct smbcli_composite *c,
167                                                 struct smbcli_session *session, 
168                                                 struct smb_composite_sesssetup *io) 
169 {
170         struct sesssetup_state *state = c->private;
171
172         state->setup.nt1.level           = RAW_SESSSETUP_NT1;
173         state->setup.nt1.in.bufsize      = session->transport->options.max_xmit;
174         state->setup.nt1.in.mpx_max      = session->transport->options.max_mux;
175         state->setup.nt1.in.vc_num       = 1;
176         state->setup.nt1.in.sesskey      = io->in.sesskey;
177         state->setup.nt1.in.capabilities = io->in.capabilities;
178         state->setup.nt1.in.domain       = io->in.domain;
179         state->setup.nt1.in.user         = io->in.user;
180         state->setup.nt1.in.os           = "Unix";
181         state->setup.nt1.in.lanman       = "Samba";
182
183         if (!io->in.password) {
184                 state->setup.nt1.in.password1 = data_blob(NULL, 0);
185                 state->setup.nt1.in.password2 = data_blob(NULL, 0);
186         } else if (session->transport->negotiate.sec_mode & 
187                    NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
188                 state->setup.nt1.in.password1 = lanman_blob(state, io->in.password, 
189                                                             session->transport->negotiate.secblob);
190                 state->setup.nt1.in.password2 = nt_blob(state, io->in.password, 
191                                                         session->transport->negotiate.secblob);
192                 use_nt1_session_keys(session, io->in.password, &state->setup.nt1.in.password2);
193         } else {
194                 state->setup.nt1.in.password1 = data_blob_talloc(state, io->in.password, strlen(io->in.password));
195                 state->setup.nt1.in.password2 = data_blob(NULL, 0);
196         }
197
198         return smb_raw_session_setup_send(session, &state->setup);
199 }
200
201
202 /*
203   old style session setup (pre NT1 protocol level)
204 */
205 static struct smbcli_request *session_setup_old(struct smbcli_composite *c,
206                                                 struct smbcli_session *session,
207                                                 struct smb_composite_sesssetup *io)
208 {
209         struct sesssetup_state *state = c->private;
210
211         state->setup.old.level      = RAW_SESSSETUP_OLD;
212         state->setup.old.in.bufsize = session->transport->options.max_xmit;
213         state->setup.old.in.mpx_max = session->transport->options.max_mux;
214         state->setup.old.in.vc_num  = 1;
215         state->setup.old.in.sesskey = io->in.sesskey;
216         state->setup.old.in.domain  = io->in.domain;
217         state->setup.old.in.user    = io->in.user;
218         state->setup.old.in.os      = "Unix";
219         state->setup.old.in.lanman  = "Samba";
220         
221         if (!io->in.password) {
222                 state->setup.old.in.password = data_blob(NULL, 0);
223         } else if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
224                 state->setup.old.in.password = lanman_blob(state, io->in.password, 
225                                                            session->transport->negotiate.secblob);
226         } else {
227                 state->setup.old.in.password = data_blob_talloc(state,
228                                                                 io->in.password, 
229                                                                 strlen(io->in.password));
230         }
231         
232         return smb_raw_session_setup_send(session, &state->setup);
233 }
234
235
236 /*
237   old style session setup (pre NT1 protocol level)
238 */
239 static struct smbcli_request *session_setup_spnego(struct smbcli_composite *c, 
240                                                    struct smbcli_session *session,
241                                                    struct smb_composite_sesssetup *io)
242 {
243         struct sesssetup_state *state = c->private;
244         NTSTATUS status;
245         DATA_BLOB session_key = data_blob(NULL, 0);
246         DATA_BLOB null_data_blob = data_blob(NULL, 0);
247         const char *chosen_oid = NULL;
248
249         state->setup.spnego.level           = RAW_SESSSETUP_SPNEGO;
250         state->setup.spnego.in.bufsize      = session->transport->options.max_xmit;
251         state->setup.spnego.in.mpx_max      = session->transport->options.max_mux;
252         state->setup.spnego.in.vc_num       = 1;
253         state->setup.spnego.in.sesskey      = io->in.sesskey;
254         state->setup.spnego.in.capabilities = io->in.capabilities;
255         state->setup.spnego.in.domain       = io->in.domain;
256         state->setup.spnego.in.os           = "Unix";
257         state->setup.spnego.in.lanman       = "Samba";
258         state->setup.spnego.out.vuid        = session->vuid;
259
260         smbcli_temp_set_signing(session->transport);
261
262         status = gensec_client_start(session, &session->gensec);
263         if (!NT_STATUS_IS_OK(status)) {
264                 DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
265                 return NULL;
266         }
267
268         gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
269
270         status = gensec_set_domain(session->gensec, io->in.domain);
271         if (!NT_STATUS_IS_OK(status)) {
272                 DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n", 
273                           io->in.domain, nt_errstr(status)));
274                 return NULL;
275         }
276
277         status = gensec_set_username(session->gensec, io->in.user);
278         if (!NT_STATUS_IS_OK(status)) {
279                 DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n", 
280                           io->in.user, nt_errstr(status)));
281                 return NULL;
282         }
283
284         status = gensec_set_password(session->gensec, io->in.password);
285         if (!NT_STATUS_IS_OK(status)) {
286                 DEBUG(1, ("Failed to start set GENSEC client password: %s\n", 
287                           nt_errstr(status)));
288                 return NULL;
289         }
290
291         status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname);
292         if (!NT_STATUS_IS_OK(status)) {
293                 DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", 
294                           nt_errstr(status)));
295                 return NULL;
296         }
297
298         if (session->transport->negotiate.secblob.length) {
299                 chosen_oid = GENSEC_OID_SPNEGO;
300         } else {
301                 /* without a sec blob, means raw NTLMSSP */
302                 chosen_oid = GENSEC_OID_NTLMSSP;
303         }
304
305         status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
306         if (!NT_STATUS_IS_OK(status)) {
307                 DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism %s: %s\n",
308                           gensec_get_name_by_oid(chosen_oid), nt_errstr(status)));
309                 return NULL;
310         }
311         
312         status = gensec_update(session->gensec, state,
313                                session->transport->negotiate.secblob,
314                                &state->setup.spnego.in.secblob);
315         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
316                 DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n",
317                           gensec_get_name_by_oid(chosen_oid), nt_errstr(status)));
318                 return NULL;
319         }
320
321         state->session_key_err = gensec_session_key(session->gensec, &session_key);
322         if (NT_STATUS_IS_OK(state->session_key_err)) {
323                 smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
324         }
325
326         return smb_raw_session_setup_send(session, &state->setup);
327 }
328
329
330 /*
331   composite session setup function that hides the details of all the
332   different session setup varients, including the multi-pass nature of
333   the spnego varient
334 */
335 struct smbcli_composite *smb_composite_sesssetup_send(struct smbcli_session *session, 
336                                                       struct smb_composite_sesssetup *io)
337 {
338         struct smbcli_composite *c;
339         struct sesssetup_state *state;
340         struct smbcli_request *req = NULL;
341
342         c = talloc_zero(session, struct smbcli_composite);
343         if (c == NULL) goto failed;
344
345         state = talloc(c, struct sesssetup_state);
346         if (state == NULL) goto failed;
347
348         c->state = SMBCLI_REQUEST_SEND;
349         c->req_parms = io;
350         c->private = state;
351         c->event_ctx = session->transport->socket->event.ctx;
352         c->composite_parms = io;
353
354         /* no session setup at all in earliest protocol varients */
355         if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
356                 ZERO_STRUCT(io->out);
357                 c->state = SMBCLI_REQUEST_DONE;
358                 return c;
359         }
360
361         /* see what session setup interface we will use */
362         if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
363                 req = session_setup_old(c, session, io);
364         } else if (!session->transport->options.use_spnego ||
365                    !(io->in.capabilities & CAP_EXTENDED_SECURITY)) {
366                 req = session_setup_nt1(c, session, io);
367         } else {
368                 req = session_setup_spnego(c, session, io);
369         }
370
371         if (req == NULL) goto failed;
372
373         req->async.fn = request_handler;
374         req->async.private = c;
375         c->req = req;
376
377         return c;
378
379 failed:
380         talloc_free(c);
381         return NULL;
382 }
383
384
385 /*
386   receive a composite session setup reply
387 */
388 NTSTATUS smb_composite_sesssetup_recv(struct smbcli_composite *c)
389 {
390         NTSTATUS status;
391         status = smb_composite_wait(c);
392         talloc_free(c);
393         return status;
394 }
395
396 /*
397   sync version of smb_composite_sesssetup 
398 */
399 NTSTATUS smb_composite_sesssetup(struct smbcli_session *session, struct smb_composite_sesssetup *io)
400 {
401         struct smbcli_composite *c = smb_composite_sesssetup_send(session, io);
402         return smb_composite_sesssetup_recv(c);
403 }