10c8f9853a0cf0693aed7579f27f43c3a54865ff
[jelmer/samba4-debian.git] / source / auth / gensec / schannel.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    dcerpc schannel operations
5
6    Copyright (C) Andrew Tridgell 2004
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
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 "librpc/gen_ndr/ndr_schannel.h"
26 #include "auth/auth.h"
27 #include "auth/gensec/schannel.h"
28
29 static size_t schannel_sig_size(struct gensec_security *gensec_security, size_t data_size)
30 {
31         return 32;
32 }
33
34 static NTSTATUS schannel_session_key(struct gensec_security *gensec_security, 
35                                             DATA_BLOB *session_key)
36 {
37         return NT_STATUS_NOT_IMPLEMENTED;
38 }
39
40 static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, 
41                                        const DATA_BLOB in, DATA_BLOB *out) 
42 {
43         struct schannel_state *state = gensec_security->private_data;
44         NTSTATUS status;
45         struct schannel_bind bind_schannel;
46         struct schannel_bind_ack bind_schannel_ack;
47         struct creds_CredentialState *creds;
48
49         const char *workstation;
50         const char *domain;
51         *out = data_blob(NULL, 0);
52
53         switch (gensec_security->gensec_role) {
54         case GENSEC_CLIENT:
55                 if (state->state != SCHANNEL_STATE_START) {
56                         /* we could parse the bind ack, but we don't know what it is yet */
57                         return NT_STATUS_OK;
58                 }
59
60                 state->creds = talloc_reference(state, cli_credentials_get_netlogon_creds(gensec_security->credentials));
61
62                 bind_schannel.unknown1 = 0;
63 #if 0
64                 /* to support this we'd need to have access to the full domain name */
65                 bind_schannel.bind_type = 23;
66                 bind_schannel.u.info23.domain = cli_credentials_get_domain(gensec_security->credentials);
67                 bind_schannel.u.info23.workstation = cli_credentials_get_workstation(gensec_security->credentials);
68                 bind_schannel.u.info23.dnsdomain = cli_credentials_get_realm(gensec_security->credentials);
69                 /* w2k3 refuses us if we use the full DNS workstation?
70                  why? perhaps because we don't fill in the dNSHostName
71                  attribute in the machine account? */
72                 bind_schannel.u.info23.dnsworkstation = cli_credentials_get_workstation(gensec_security->credentials);
73 #else
74                 bind_schannel.bind_type = 3;
75                 bind_schannel.u.info3.domain = cli_credentials_get_domain(gensec_security->credentials);
76                 bind_schannel.u.info3.workstation = cli_credentials_get_workstation(gensec_security->credentials);
77 #endif
78                 
79                 status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
80                                               (ndr_push_flags_fn_t)ndr_push_schannel_bind);
81                 if (!NT_STATUS_IS_OK(status)) {
82                         DEBUG(3, ("Could not create schannel bind: %s\n",
83                                   nt_errstr(status)));
84                         return status;
85                 }
86                 
87                 state->state = SCHANNEL_STATE_UPDATE_1;
88
89                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
90         case GENSEC_SERVER:
91                 
92                 if (state->state != SCHANNEL_STATE_START) {
93                         /* no third leg on this protocol */
94                         return NT_STATUS_INVALID_PARAMETER;
95                 }
96                 
97                 /* parse the schannel startup blob */
98                 status = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel, 
99                                               (ndr_pull_flags_fn_t)ndr_pull_schannel_bind);
100                 if (!NT_STATUS_IS_OK(status)) {
101                         return status;
102                 }
103                 
104                 if (bind_schannel.bind_type == 23) {
105                         workstation = bind_schannel.u.info23.workstation;
106                         domain = bind_schannel.u.info23.domain;
107                 } else {
108                         workstation = bind_schannel.u.info3.workstation;
109                         domain = bind_schannel.u.info3.domain;
110                 }
111                 
112                 /* pull the session key for this client */
113                 status = schannel_fetch_session_key(out_mem_ctx, workstation, 
114                                                     domain, &creds);
115                 if (!NT_STATUS_IS_OK(status)) {
116                         DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n",
117                                   workstation, nt_errstr(status)));
118                         return status;
119                 }
120
121                 state->creds = talloc_reference(state, creds);
122
123                 bind_schannel_ack.unknown1 = 1;
124                 bind_schannel_ack.unknown2 = 0;
125                 bind_schannel_ack.unknown3 = 0x6c0000;
126                 
127                 status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack, 
128                                               (ndr_push_flags_fn_t)ndr_push_schannel_bind_ack);
129                 if (!NT_STATUS_IS_OK(status)) {
130                         DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n",
131                                   workstation, nt_errstr(status)));
132                         return status;
133                 }
134
135                 state->state = SCHANNEL_STATE_UPDATE_1;
136
137                 return NT_STATUS_OK;
138         }
139         return NT_STATUS_INVALID_PARAMETER;
140 }
141
142 /**
143  * Return the struct creds_CredentialState.
144  *
145  * Make sure not to call this unless gensec is using schannel...
146  */
147
148 NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
149                                TALLOC_CTX *mem_ctx,
150                                struct creds_CredentialState **creds)
151
152         struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
153
154         *creds = talloc_reference(mem_ctx, state->creds);
155         if (!*creds) {
156                 return NT_STATUS_NO_MEMORY;
157         }
158         return NT_STATUS_OK;
159 }
160                 
161
162 /** 
163  * Returns anonymous credentials for schannel, matching Win2k3.
164  *
165  */
166
167 static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
168                                          struct auth_session_info **_session_info) 
169 {
170         struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
171         return auth_anonymous_session_info(state, _session_info);
172 }
173
174 static NTSTATUS schannel_start(struct gensec_security *gensec_security)
175 {
176         struct schannel_state *state;
177
178         state = talloc(gensec_security, struct schannel_state);
179         if (!state) {
180                 return NT_STATUS_NO_MEMORY;
181         }
182
183         state->state = SCHANNEL_STATE_START;
184         state->seq_num = 0;
185         gensec_security->private_data = state;
186
187         return NT_STATUS_OK;
188 }
189
190 static NTSTATUS schannel_server_start(struct gensec_security *gensec_security) 
191 {
192         NTSTATUS status;
193         struct schannel_state *state;
194
195         status = schannel_start(gensec_security);
196         if (!NT_STATUS_IS_OK(status)) {
197                 return status;
198         }
199
200         state = gensec_security->private_data;
201         state->initiator = False;
202                 
203         return NT_STATUS_OK;
204 }
205
206 static NTSTATUS schannel_client_start(struct gensec_security *gensec_security) 
207 {
208         NTSTATUS status;
209         struct schannel_state *state;
210
211         status = schannel_start(gensec_security);
212         if (!NT_STATUS_IS_OK(status)) {
213                 return status;
214         }
215
216         state = gensec_security->private_data;
217         state->initiator = True;
218                 
219         return NT_STATUS_OK;
220 }
221
222
223 static BOOL schannel_have_feature(struct gensec_security *gensec_security,
224                                          uint32_t feature)
225 {
226         if (feature & (GENSEC_FEATURE_SIGN | 
227                        GENSEC_FEATURE_SEAL)) {
228                 return True;
229         }
230         if (feature & GENSEC_FEATURE_DCE_STYLE) {
231                 return True;
232         }
233         if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
234                 return True;
235         }
236         return False;
237 }
238
239
240 static const struct gensec_security_ops gensec_schannel_security_ops = {
241         .name           = "schannel",
242         .auth_type      = DCERPC_AUTH_TYPE_SCHANNEL,
243         .client_start   = schannel_client_start,
244         .server_start   = schannel_server_start,
245         .update         = schannel_update,
246         .seal_packet    = schannel_seal_packet,
247         .sign_packet    = schannel_sign_packet,
248         .check_packet   = schannel_check_packet,
249         .unseal_packet  = schannel_unseal_packet,
250         .session_key    = schannel_session_key,
251         .session_info   = schannel_session_info,
252         .sig_size       = schannel_sig_size,
253         .have_feature   = schannel_have_feature,
254         .enabled        = True
255 };
256
257 NTSTATUS gensec_schannel_init(void)
258 {
259         NTSTATUS ret;
260         ret = gensec_register(&gensec_schannel_security_ops);
261         if (!NT_STATUS_IS_OK(ret)) {
262                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
263                         gensec_schannel_security_ops.name));
264                 return ret;
265         }
266
267         return ret;
268 }