r10153: This patch adds a new parameter to gensec_sig_size(), the size of the
[ira/wip.git] / source4 / 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 = gensec_security->private_data;
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  * Return the credentials of a logged on user, including session keys
164  * etc.
165  *
166  * Only valid after a successful authentication
167  *
168  * May only be called once per authentication.
169  *
170  */
171
172 static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
173                                       struct auth_session_info **session_info)
174 {
175         (*session_info) = talloc(gensec_security, struct auth_session_info);
176         NT_STATUS_HAVE_NO_MEMORY(*session_info);
177
178         ZERO_STRUCTP(*session_info);
179
180         return NT_STATUS_OK;
181 }
182
183 static NTSTATUS schannel_start(struct gensec_security *gensec_security)
184 {
185         struct schannel_state *state;
186
187         state = talloc(gensec_security, struct schannel_state);
188         if (!state) {
189                 return NT_STATUS_NO_MEMORY;
190         }
191
192         state->state = SCHANNEL_STATE_START;
193         state->seq_num = 0;
194         gensec_security->private_data = state;
195
196         return NT_STATUS_OK;
197 }
198
199 static NTSTATUS schannel_server_start(struct gensec_security *gensec_security) 
200 {
201         NTSTATUS status;
202         struct schannel_state *state;
203
204         status = schannel_start(gensec_security);
205         if (!NT_STATUS_IS_OK(status)) {
206                 return status;
207         }
208
209         state = gensec_security->private_data;
210         state->initiator = False;
211                 
212         return NT_STATUS_OK;
213 }
214
215 static NTSTATUS schannel_client_start(struct gensec_security *gensec_security) 
216 {
217         NTSTATUS status;
218         struct schannel_state *state;
219
220         status = schannel_start(gensec_security);
221         if (!NT_STATUS_IS_OK(status)) {
222                 return status;
223         }
224
225         state = gensec_security->private_data;
226         state->initiator = True;
227                 
228         return NT_STATUS_OK;
229 }
230
231
232 static BOOL schannel_have_feature(struct gensec_security *gensec_security,
233                                          uint32_t feature)
234 {
235         if (feature & (GENSEC_FEATURE_SIGN | 
236                        GENSEC_FEATURE_SEAL)) {
237                 return True;
238         }
239         if (feature & GENSEC_FEATURE_DCE_STYLE) {
240                 return True;
241         }
242         if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
243                 return True;
244         }
245         return False;
246 }
247
248
249 static const struct gensec_security_ops gensec_schannel_security_ops = {
250         .name           = "schannel",
251         .auth_type      = DCERPC_AUTH_TYPE_SCHANNEL,
252         .client_start   = schannel_client_start,
253         .server_start   = schannel_server_start,
254         .update         = schannel_update,
255         .seal_packet    = schannel_seal_packet,
256         .sign_packet    = schannel_sign_packet,
257         .check_packet   = schannel_check_packet,
258         .unseal_packet  = schannel_unseal_packet,
259         .session_key    = schannel_session_key,
260         .session_info   = schannel_session_info,
261         .sig_size       = schannel_sig_size,
262         .have_feature   = schannel_have_feature,
263         .enabled        = True
264 };
265
266 NTSTATUS gensec_schannel_init(void)
267 {
268         NTSTATUS ret;
269         ret = gensec_register(&gensec_schannel_security_ops);
270         if (!NT_STATUS_IS_OK(ret)) {
271                 DEBUG(0,("Failed to register '%s' gensec backend!\n",
272                         gensec_schannel_security_ops.name));
273                 return ret;
274         }
275
276         return ret;
277 }