85fac5680bb4c6b896dad2dba02d1f3ac25f70c6
[samba.git] / source4 / smb_server / sesssetup.c
1 /* 
2    Unix SMB/CIFS implementation.
3    handle SMBsessionsetup
4    Copyright (C) Andrew Tridgell 1998-2001
5    Copyright (C) Andrew Bartlett      2001
6    Copyright (C) Jim McDonough        2002
7    Copyright (C) Luke Howard          2003
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "auth/auth.h"
26 #include "smb_server/smb_server.h"
27
28
29 /*
30   setup the OS, Lanman and domain portions of a session setup reply
31 */
32 static void sesssetup_common_strings(struct smbsrv_request *req,
33                                      char **os, char **lanman, char **domain)
34 {
35         (*os) = talloc_asprintf(req, "Unix");
36         (*lanman) = talloc_asprintf(req, "Samba %s", SAMBA_VERSION_STRING);
37         (*domain) = talloc_asprintf(req, "%s", lp_workgroup());
38 }
39
40
41 /*
42   handler for old style session setup
43 */
44 static NTSTATUS sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
45 {
46         NTSTATUS status;
47         struct auth_usersupplied_info *user_info = NULL;
48         struct auth_serversupplied_info *server_info = NULL;
49         struct auth_session_info *session_info;
50
51         TALLOC_CTX *mem_ctx = talloc_named(req, 0, "NT1 session setup");
52         char *remote_machine;
53         if (!mem_ctx) {
54                 return NT_STATUS_NO_MEMORY;
55         }
56         
57         if (!req->smb_conn->negotiate.done_sesssetup) {
58                 req->smb_conn->negotiate.max_send = sess->old.in.bufsize;
59         }
60         
61         remote_machine = socket_get_peer_addr(req->smb_conn->connection->socket, mem_ctx);
62         status = make_user_info_for_reply_enc(req->smb_conn,
63                                               &user_info, 
64                                               sess->old.in.user, sess->old.in.domain,
65                                               remote_machine,
66                                               sess->old.in.password,
67                                               data_blob(NULL, 0));
68         if (!NT_STATUS_IS_OK(status)) {
69                 talloc_free(mem_ctx);
70                 return NT_STATUS_ACCESS_DENIED;
71         }
72
73         status = req->smb_conn->negotiate.auth_context->check_ntlm_password(req->smb_conn->negotiate.auth_context, 
74                                                                             user_info, 
75                                                                             mem_ctx, 
76                                                                             &server_info);
77         if (!NT_STATUS_IS_OK(status)) {
78                 talloc_free(mem_ctx);
79                 return nt_status_squash(status);
80         }
81
82         /* This references server_info into session_info */
83         status = make_session_info(req, server_info, &session_info);
84         talloc_free(mem_ctx);
85         if (!NT_STATUS_IS_OK(status)) {
86                 return nt_status_squash(status);
87         }
88
89         sess->old.out.action = 0;
90         sess->old.out.vuid = smbsrv_register_session(req->smb_conn, session_info, NULL);
91         if (sess->old.out.vuid == UID_FIELD_INVALID) {
92                 return NT_STATUS_ACCESS_DENIED;
93         }
94         sesssetup_common_strings(req, 
95                                  &sess->old.out.os,
96                                  &sess->old.out.lanman,
97                                  &sess->old.out.domain);
98
99         req->session = smbsrv_session_find(req->smb_conn, sess->old.out.vuid);
100
101         return NT_STATUS_OK;
102 }
103
104
105 /*
106   handler for NT1 style session setup
107 */
108 static NTSTATUS sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
109 {
110         NTSTATUS status;
111         struct auth_usersupplied_info *user_info = NULL;
112         struct auth_serversupplied_info *server_info = NULL;
113         struct auth_session_info *session_info;
114         TALLOC_CTX *mem_ctx = talloc_named(req, 0, "NT1 session setup");
115         
116         if (!mem_ctx) {
117                 return NT_STATUS_NO_MEMORY;
118         }
119
120         if (!req->smb_conn->negotiate.done_sesssetup) {
121                 req->smb_conn->negotiate.max_send = sess->nt1.in.bufsize;
122                 req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities;
123         }
124
125         if (req->smb_conn->negotiate.spnego_negotiated) {
126                 struct auth_context *auth_context;
127
128                 if (sess->nt1.in.user && *sess->nt1.in.user) {
129                         return NT_STATUS_ACCESS_DENIED;
130                 } else {
131                         make_user_info_guest(req->smb_conn, &user_info);
132                 }
133                 
134                 status = make_auth_context_subsystem(req->smb_conn, &auth_context);
135
136                 if (!NT_STATUS_IS_OK(status)) {
137                         talloc_free(mem_ctx);
138                         return status;
139                 }
140                 
141                 status = auth_context->check_ntlm_password(auth_context, 
142                                                            user_info, 
143                                                            mem_ctx,
144                                                            &server_info);
145         } else {
146                 char *remote_machine;
147                 remote_machine = socket_get_peer_addr(req->smb_conn->connection->socket, mem_ctx);
148                 status = make_user_info_for_reply_enc(req->smb_conn,
149                                                       &user_info, 
150                                                       sess->nt1.in.user, sess->nt1.in.domain,
151                                                       remote_machine,
152                                                       sess->nt1.in.password1,
153                                                       sess->nt1.in.password2);
154                 if (!NT_STATUS_IS_OK(status)) {
155                         talloc_free(mem_ctx);
156                         return NT_STATUS_ACCESS_DENIED;
157                 }
158                 
159                 status = req->smb_conn->negotiate
160                         .auth_context->check_ntlm_password(req->smb_conn->negotiate
161                                                            .auth_context, 
162                                                            user_info, 
163                                                            req,
164                                                            &server_info);
165         }
166
167         if (!NT_STATUS_IS_OK(status)) {
168                 talloc_free(mem_ctx);
169                 return nt_status_squash(status);
170         }
171
172         /* This references server_info into session_info */
173         status = make_session_info(mem_ctx, server_info, &session_info);
174         if (!NT_STATUS_IS_OK(status)) {
175                 talloc_free(mem_ctx);
176                 return nt_status_squash(status);
177         }
178
179         sess->nt1.out.action = 0;
180         sess->nt1.out.vuid = smbsrv_register_session(req->smb_conn, session_info, NULL);
181         talloc_free(mem_ctx);
182         if (sess->nt1.out.vuid == UID_FIELD_INVALID) {
183                 return NT_STATUS_ACCESS_DENIED;
184         }
185         sesssetup_common_strings(req, 
186                                  &sess->nt1.out.os,
187                                  &sess->nt1.out.lanman,
188                                  &sess->nt1.out.domain);
189         
190         req->session = smbsrv_session_find(req->smb_conn, sess->nt1.out.vuid);
191         if (session_info->server_info->guest) {
192                 return NT_STATUS_OK;
193         }
194         if (!srv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) {
195                 /* Already signing, or disabled */
196                 return NT_STATUS_OK;
197         }
198                 
199         /* Force check of the request packet, now we know the session key */
200         req_signing_check_incoming(req);
201
202         srv_signing_restart(req->smb_conn,  &session_info->session_key, &sess->nt1.in.password2);
203
204         return NT_STATUS_OK;
205 }
206
207
208 /*
209   handler for SPNEGO style session setup
210 */
211 static NTSTATUS sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *sess)
212 {
213         NTSTATUS status = NT_STATUS_ACCESS_DENIED;
214         struct smbsrv_session *smb_sess;
215         struct gensec_security *gensec_ctx = NULL;
216         struct auth_session_info *session_info = NULL;
217         uint16_t vuid;
218
219         if (!req->smb_conn->negotiate.done_sesssetup) {
220                 req->smb_conn->negotiate.max_send = sess->nt1.in.bufsize;
221                 req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities;
222         }
223
224         vuid = SVAL(req->in.hdr,HDR_UID);
225         smb_sess = smbsrv_session_find(req->smb_conn, vuid);
226         if (smb_sess && !smb_sess->session_info) {
227                 if (!smb_sess->gensec_ctx) {
228                         return NT_STATUS_INVALID_HANDLE;
229                 }
230
231                 status = gensec_update(smb_sess->gensec_ctx, req, sess->spnego.in.secblob, &sess->spnego.out.secblob);
232         } else {
233                 smb_sess = NULL;
234
235                 status = gensec_server_start(req->smb_conn, &gensec_ctx);
236                 if (!NT_STATUS_IS_OK(status)) {
237                         DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
238                         return status;
239                 }
240
241                 gensec_set_target_service(gensec_ctx, "cifs");
242
243                 gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
244
245                 status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
246                 if (!NT_STATUS_IS_OK(status)) {
247                         DEBUG(1, ("Failed to start GENSEC SPNEGO server code: %s\n", nt_errstr(status)));
248                         return status;
249                 }
250
251                 status = gensec_update(gensec_ctx, req, sess->spnego.in.secblob, &sess->spnego.out.secblob);
252
253         }
254
255         if (!smb_sess) {
256                 vuid = smbsrv_register_session(req->smb_conn, 
257                                                session_info, gensec_ctx);
258                 if (vuid == UID_FIELD_INVALID) {
259                         return NT_STATUS_ACCESS_DENIED;
260                 }
261                 smb_sess = smbsrv_session_find(req->smb_conn, vuid);
262                 if (!smb_sess) {
263                         return NT_STATUS_FOOBAR;
264                 }
265         }
266
267         if (NT_STATUS_IS_OK(status)) {
268                 DATA_BLOB session_key;
269                 
270                 status = gensec_session_info(smb_sess->gensec_ctx, &smb_sess->session_info);
271                 if (!NT_STATUS_IS_OK(status)) {
272                         return status;
273                 }
274                 
275                 status = gensec_session_key(smb_sess->gensec_ctx, 
276                                             &session_key);
277                 if (NT_STATUS_IS_OK(status) 
278                     && !smb_sess->session_info->server_info->guest
279                     && srv_setup_signing(req->smb_conn, &session_key, NULL)) {
280                         /* Force check of the request packet, now we know the session key */
281                         req_signing_check_incoming(req);
282
283                         srv_signing_restart(req->smb_conn, &session_key, NULL);
284
285                 }
286         } else {
287                 status = nt_status_squash(status);
288                 if (smb_sess->gensec_ctx && 
289                     !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
290                         gensec_end(&smb_sess->gensec_ctx);
291                 }
292         }
293
294         sess->spnego.out.action = 0;
295         sess->spnego.out.vuid = vuid;
296         sesssetup_common_strings(req, 
297                                  &sess->spnego.out.os,
298                                  &sess->spnego.out.lanman,
299                                  &sess->spnego.out.domain);
300
301         return status;
302 }
303
304 /*
305   backend for sessionsetup call - this takes all 3 variants of the call
306 */
307 NTSTATUS sesssetup_backend(struct smbsrv_request *req, 
308                            union smb_sesssetup *sess)
309 {
310         NTSTATUS status = NT_STATUS_INVALID_LEVEL;
311
312         switch (sess->generic.level) {
313                 case RAW_SESSSETUP_GENERIC:
314                         status = NT_STATUS_INVALID_LEVEL;
315                         break;
316                 case RAW_SESSSETUP_OLD:
317                         status = sesssetup_old(req, sess);
318                         break;
319                 case RAW_SESSSETUP_NT1:
320                         status = sesssetup_nt1(req, sess);
321                         break;
322                 case RAW_SESSSETUP_SPNEGO:
323                         status = sesssetup_spnego(req, sess);
324                         break;
325         }
326
327         if (NT_STATUS_IS_OK(status)) {
328                 req->smb_conn->negotiate.done_sesssetup = True;
329         }
330
331         return status;
332 }
333
334