r4783: got rid of another void* in the composite code. This brings us down to
[ira/wip.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         struct smb_composite_sesssetup *io;
34         struct smbcli_request *req;
35 };
36
37
38 /*
39   form an encrypted lanman password from a plaintext password
40   and the server supplied challenge
41 */
42 static DATA_BLOB lanman_blob(TALLOC_CTX *mem_ctx, const char *pass, DATA_BLOB challenge)
43 {
44         DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
45         SMBencrypt(pass, challenge.data, blob.data);
46         return blob;
47 }
48
49 /*
50   form an encrypted NT password from a plaintext password
51   and the server supplied challenge
52 */
53 static DATA_BLOB nt_blob(TALLOC_CTX *mem_ctx, const char *pass, DATA_BLOB challenge)
54 {
55         DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
56         SMBNTencrypt(pass, challenge.data, blob.data);
57         return blob;
58 }
59
60 /*
61   store the user session key for a transport
62 */
63 static void set_user_session_key(struct smbcli_session *session,
64                                  const DATA_BLOB *session_key)
65 {
66         session->user_session_key = data_blob_talloc(session, 
67                                                      session_key->data, 
68                                                      session_key->length);
69 }
70
71 /*
72   setup signing for a NT1 style session setup
73 */
74 static void use_nt1_session_keys(struct smbcli_session *session, 
75                                  const char *password, const DATA_BLOB *nt_response)
76 {
77         struct smbcli_transport *transport = session->transport; 
78         uint8_t nt_hash[16];
79         DATA_BLOB session_key = data_blob_talloc(session, NULL, 16);
80
81         E_md4hash(password, nt_hash);
82         SMBsesskeygen_ntv1(nt_hash, session_key.data);
83
84         smbcli_transport_simple_set_signing(transport, session_key, *nt_response);
85
86         set_user_session_key(session, &session_key);
87         data_blob_free(&session_key);
88 }
89
90
91 /*
92   handler for completion of a smbcli_request sub-request
93 */
94 static void request_handler(struct smbcli_request *req)
95 {
96         struct smbcli_composite *c = req->async.private;
97         struct sesssetup_state *state = c->private;
98         struct smbcli_session *session = req->session;
99         DATA_BLOB session_key = data_blob(NULL, 0);
100         DATA_BLOB null_data_blob = data_blob(NULL, 0);
101
102         c->status = smb_raw_session_setup_recv(req, state, &state->setup);
103
104         switch (state->setup.old.level) {
105         case RAW_SESSSETUP_OLD:
106                 state->io->out.vuid = state->setup.old.out.vuid;
107                 break;
108
109         case RAW_SESSSETUP_NT1:
110                 state->io->out.vuid = state->setup.nt1.out.vuid;
111                 break;
112
113         case RAW_SESSSETUP_SPNEGO:
114                 session->vuid = state->io->out.vuid = state->setup.spnego.out.vuid;
115                 if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && 
116                     !NT_STATUS_IS_OK(c->status)) {
117                         break;
118                 }
119                 c->status = gensec_update(session->gensec, state,
120                                           state->setup.spnego.out.secblob,
121                                           &state->setup.spnego.in.secblob);
122                 if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && 
123                     !NT_STATUS_IS_OK(c->status)) {
124                         break;
125                 }
126                 if (state->setup.spnego.in.secblob.length == 0) {
127                         break;
128                 }
129
130                 /* we need to do another round of session setup. We keep going until both sides
131                    are happy */
132                 state->session_key_err = gensec_session_key(session->gensec, &session_key);
133                 if (NT_STATUS_IS_OK(state->session_key_err)) {
134                         set_user_session_key(session, &session_key);
135                         smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
136                 }
137
138                 state->req = smb_raw_session_setup_send(session, &state->setup);
139                 state->req->async.fn = request_handler;
140                 state->req->async.private = c;
141                 return;
142         }
143
144         /* enforce the local signing required flag */
145         if (NT_STATUS_IS_OK(c->status) && state->io->in.user && state->io->in.user[0]) {
146                 if (!session->transport->negotiate.sign_info.doing_signing 
147                     && session->transport->negotiate.sign_info.mandatory_signing) {
148                         DEBUG(0, ("SMB signing required, but server does not support it\n"));
149                         c->status = NT_STATUS_ACCESS_DENIED;
150                 }
151         }
152
153         if (NT_STATUS_IS_OK(c->status)) {
154                 c->state = SMBCLI_REQUEST_DONE;
155         } else {
156                 c->state = SMBCLI_REQUEST_ERROR;
157         }
158         if (c->async.fn) {
159                 c->async.fn(c);
160         }
161 }
162
163
164 /*
165   send a nt1 style session setup
166 */
167 static struct smbcli_request *session_setup_nt1(struct smbcli_composite *c,
168                                                 struct smbcli_session *session, 
169                                                 struct smb_composite_sesssetup *io) 
170 {
171         struct sesssetup_state *state = c->private;
172
173         state->setup.nt1.level           = RAW_SESSSETUP_NT1;
174         state->setup.nt1.in.bufsize      = session->transport->options.max_xmit;
175         state->setup.nt1.in.mpx_max      = session->transport->options.max_mux;
176         state->setup.nt1.in.vc_num       = 1;
177         state->setup.nt1.in.sesskey      = io->in.sesskey;
178         state->setup.nt1.in.capabilities = io->in.capabilities;
179         state->setup.nt1.in.domain       = io->in.domain;
180         state->setup.nt1.in.user         = io->in.user;
181         state->setup.nt1.in.os           = "Unix";
182         state->setup.nt1.in.lanman       = "Samba";
183
184         if (!io->in.password) {
185                 state->setup.nt1.in.password1 = data_blob(NULL, 0);
186                 state->setup.nt1.in.password2 = data_blob(NULL, 0);
187         } else if (session->transport->negotiate.sec_mode & 
188                    NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
189                 state->setup.nt1.in.password1 = lanman_blob(state, io->in.password, 
190                                                             session->transport->negotiate.secblob);
191                 state->setup.nt1.in.password2 = nt_blob(state, io->in.password, 
192                                                         session->transport->negotiate.secblob);
193                 use_nt1_session_keys(session, io->in.password, &state->setup.nt1.in.password2);
194         } else {
195                 state->setup.nt1.in.password1 = data_blob_talloc(state, io->in.password, strlen(io->in.password));
196                 state->setup.nt1.in.password2 = data_blob(NULL, 0);
197         }
198
199         return smb_raw_session_setup_send(session, &state->setup);
200 }
201
202
203 /*
204   old style session setup (pre NT1 protocol level)
205 */
206 static struct smbcli_request *session_setup_old(struct smbcli_composite *c,
207                                                 struct smbcli_session *session,
208                                                 struct smb_composite_sesssetup *io)
209 {
210         struct sesssetup_state *state = c->private;
211
212         state->setup.old.level      = RAW_SESSSETUP_OLD;
213         state->setup.old.in.bufsize = session->transport->options.max_xmit;
214         state->setup.old.in.mpx_max = session->transport->options.max_mux;
215         state->setup.old.in.vc_num  = 1;
216         state->setup.old.in.sesskey = io->in.sesskey;
217         state->setup.old.in.domain  = io->in.domain;
218         state->setup.old.in.user    = io->in.user;
219         state->setup.old.in.os      = "Unix";
220         state->setup.old.in.lanman  = "Samba";
221         
222         if (!io->in.password) {
223                 state->setup.old.in.password = data_blob(NULL, 0);
224         } else if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
225                 state->setup.old.in.password = lanman_blob(state, io->in.password, 
226                                                            session->transport->negotiate.secblob);
227         } else {
228                 state->setup.old.in.password = data_blob_talloc(state,
229                                                                 io->in.password, 
230                                                                 strlen(io->in.password));
231         }
232         
233         return smb_raw_session_setup_send(session, &state->setup);
234 }
235
236
237 /*
238   old style session setup (pre NT1 protocol level)
239 */
240 static struct smbcli_request *session_setup_spnego(struct smbcli_composite *c, 
241                                                    struct smbcli_session *session,
242                                                    struct smb_composite_sesssetup *io)
243 {
244         struct sesssetup_state *state = c->private;
245         NTSTATUS status;
246         DATA_BLOB session_key = data_blob(NULL, 0);
247         DATA_BLOB null_data_blob = data_blob(NULL, 0);
248         const char *chosen_oid = NULL;
249
250         state->setup.spnego.level           = RAW_SESSSETUP_SPNEGO;
251         state->setup.spnego.in.bufsize      = session->transport->options.max_xmit;
252         state->setup.spnego.in.mpx_max      = session->transport->options.max_mux;
253         state->setup.spnego.in.vc_num       = 1;
254         state->setup.spnego.in.sesskey      = io->in.sesskey;
255         state->setup.spnego.in.capabilities = io->in.capabilities;
256         state->setup.spnego.in.domain       = io->in.domain;
257         state->setup.spnego.in.os           = "Unix";
258         state->setup.spnego.in.lanman       = "Samba";
259         state->setup.spnego.out.vuid        = session->vuid;
260
261         smbcli_temp_set_signing(session->transport);
262
263         status = gensec_client_start(session, &session->gensec);
264         if (!NT_STATUS_IS_OK(status)) {
265                 DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
266                 return NULL;
267         }
268
269         gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
270
271         status = gensec_set_domain(session->gensec, io->in.domain);
272         if (!NT_STATUS_IS_OK(status)) {
273                 DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n", 
274                           io->in.domain, nt_errstr(status)));
275                 return NULL;
276         }
277
278         status = gensec_set_username(session->gensec, io->in.user);
279         if (!NT_STATUS_IS_OK(status)) {
280                 DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n", 
281                           io->in.user, nt_errstr(status)));
282                 return NULL;
283         }
284
285         status = gensec_set_password(session->gensec, io->in.password);
286         if (!NT_STATUS_IS_OK(status)) {
287                 DEBUG(1, ("Failed to start set GENSEC client password: %s\n", 
288                           nt_errstr(status)));
289                 return NULL;
290         }
291
292         status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname);
293         if (!NT_STATUS_IS_OK(status)) {
294                 DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", 
295                           nt_errstr(status)));
296                 return NULL;
297         }
298
299         if (session->transport->negotiate.secblob.length) {
300                 chosen_oid = GENSEC_OID_SPNEGO;
301         } else {
302                 /* without a sec blob, means raw NTLMSSP */
303                 chosen_oid = GENSEC_OID_NTLMSSP;
304         }
305
306         status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
307         if (!NT_STATUS_IS_OK(status)) {
308                 DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism %s: %s\n",
309                           gensec_get_name_by_oid(chosen_oid), nt_errstr(status)));
310                 return NULL;
311         }
312         
313         status = gensec_update(session->gensec, state,
314                                session->transport->negotiate.secblob,
315                                &state->setup.spnego.in.secblob);
316         if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
317                 DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n",
318                           gensec_get_name_by_oid(chosen_oid), nt_errstr(status)));
319                 return NULL;
320         }
321
322         state->session_key_err = gensec_session_key(session->gensec, &session_key);
323         if (NT_STATUS_IS_OK(state->session_key_err)) {
324                 smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
325         }
326
327         return smb_raw_session_setup_send(session, &state->setup);
328 }
329
330
331 /*
332   composite session setup function that hides the details of all the
333   different session setup varients, including the multi-pass nature of
334   the spnego varient
335 */
336 struct smbcli_composite *smb_composite_sesssetup_send(struct smbcli_session *session, 
337                                                       struct smb_composite_sesssetup *io)
338 {
339         struct smbcli_composite *c;
340         struct sesssetup_state *state;
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         state->io = io;
349
350         c->state = SMBCLI_REQUEST_SEND;
351         c->private = state;
352         c->event_ctx = session->transport->socket->event.ctx;
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                 state->req = session_setup_old(c, session, io);
364         } else if (!session->transport->options.use_spnego ||
365                    !(io->in.capabilities & CAP_EXTENDED_SECURITY)) {
366                 state->req = session_setup_nt1(c, session, io);
367         } else {
368                 state->req = session_setup_spnego(c, session, io);
369         }
370
371         if (state->req == NULL) goto failed;
372
373         state->req->async.fn = request_handler;
374         state->req->async.private = c;
375
376         return c;
377
378 failed:
379         talloc_free(c);
380         return NULL;
381 }
382
383
384 /*
385   receive a composite session setup reply
386 */
387 NTSTATUS smb_composite_sesssetup_recv(struct smbcli_composite *c)
388 {
389         NTSTATUS status;
390         status = smb_composite_wait(c);
391         talloc_free(c);
392         return status;
393 }
394
395 /*
396   sync version of smb_composite_sesssetup 
397 */
398 NTSTATUS smb_composite_sesssetup(struct smbcli_session *session, struct smb_composite_sesssetup *io)
399 {
400         struct smbcli_composite *c = smb_composite_sesssetup_send(session, io);
401         return smb_composite_sesssetup_recv(c);
402 }