r6113: Move GENSEC and the kerberos code out of libcli/auth, and into
[samba.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)
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.account_name = cli_credentials_get_username(gensec_security->credentials);
68                 bind_schannel.u.info23.dnsdomain = str_format_nbt_domain(out_mem_ctx, fulldomainname);
69                 bind_schannel.u.info23.workstation = str_format_nbt_domain(out_mem_ctx, cli_credentials_get_workstation(gensec_security->credentials));
70 #else
71                 bind_schannel.bind_type = 3;
72                 bind_schannel.u.info3.domain = cli_credentials_get_domain(gensec_security->credentials);
73                 bind_schannel.u.info3.workstation = cli_credentials_get_workstation(gensec_security->credentials);
74 #endif
75                 
76                 status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
77                                               (ndr_push_flags_fn_t)ndr_push_schannel_bind);
78                 if (!NT_STATUS_IS_OK(status)) {
79                         DEBUG(3, ("Could not create schannel bind: %s\n",
80                                   nt_errstr(status)));
81                         return status;
82                 }
83                 
84                 state->state = SCHANNEL_STATE_UPDATE_1;
85
86                 return NT_STATUS_MORE_PROCESSING_REQUIRED;
87         case GENSEC_SERVER:
88                 
89                 if (state->state != SCHANNEL_STATE_START) {
90                         /* no third leg on this protocol */
91                         return NT_STATUS_INVALID_PARAMETER;
92                 }
93                 
94                 /* parse the schannel startup blob */
95                 status = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel, 
96                                               (ndr_pull_flags_fn_t)ndr_pull_schannel_bind);
97                 if (!NT_STATUS_IS_OK(status)) {
98                         return status;
99                 }
100                 
101                 if (bind_schannel.bind_type == 23) {
102                         workstation = bind_schannel.u.info23.workstation;
103                         domain = bind_schannel.u.info23.domain;
104                 } else {
105                         workstation = bind_schannel.u.info3.workstation;
106                         domain = bind_schannel.u.info3.domain;
107                 }
108                 
109                 /* pull the session key for this client */
110                 status = schannel_fetch_session_key(out_mem_ctx, workstation, 
111                                                     domain, &creds);
112                 if (!NT_STATUS_IS_OK(status)) {
113                         DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n",
114                                   workstation, nt_errstr(status)));
115                         return status;
116                 }
117
118                 state->creds = talloc_reference(state, creds);
119
120                 bind_schannel_ack.unknown1 = 1;
121                 bind_schannel_ack.unknown2 = 0;
122                 bind_schannel_ack.unknown3 = 0x6c0000;
123                 
124                 status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack, 
125                                               (ndr_push_flags_fn_t)ndr_push_schannel_bind_ack);
126                 if (!NT_STATUS_IS_OK(status)) {
127                         DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n",
128                                   workstation, nt_errstr(status)));
129                         return status;
130                 }
131
132                 state->state = SCHANNEL_STATE_UPDATE_1;
133
134                 return NT_STATUS_OK;
135         }
136         return NT_STATUS_INVALID_PARAMETER;
137 }
138
139 /**
140  * Return the struct creds_CredentialState.
141  *
142  * Make sure not to call this unless gensec is using schannel...
143  */
144
145 NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
146                                TALLOC_CTX *mem_ctx,
147                                struct creds_CredentialState **creds)
148
149         struct schannel_state *state = gensec_security->private_data;
150
151         *creds = talloc_reference(mem_ctx, state->creds);
152         if (!*creds) {
153                 return NT_STATUS_NO_MEMORY;
154         }
155         return NT_STATUS_OK;
156 }
157                 
158
159 /** 
160  * Return the credentials of a logged on user, including session keys
161  * etc.
162  *
163  * Only valid after a successful authentication
164  *
165  * May only be called once per authentication.
166  *
167  */
168
169 static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
170                                       struct auth_session_info **session_info)
171 {
172         (*session_info) = talloc(gensec_security, struct auth_session_info);
173         NT_STATUS_HAVE_NO_MEMORY(*session_info);
174
175         ZERO_STRUCTP(*session_info);
176
177         return NT_STATUS_OK;
178 }
179
180 static NTSTATUS schannel_start(struct gensec_security *gensec_security)
181 {
182         struct schannel_state *state;
183
184         state = talloc(gensec_security, struct schannel_state);
185         if (!state) {
186                 return NT_STATUS_NO_MEMORY;
187         }
188
189         state->state = SCHANNEL_STATE_START;
190         state->seq_num = 0;
191         gensec_security->private_data = state;
192
193         return NT_STATUS_OK;
194 }
195
196 static NTSTATUS schannel_server_start(struct gensec_security *gensec_security) 
197 {
198         NTSTATUS status;
199         struct schannel_state *state;
200
201         status = schannel_start(gensec_security);
202         if (!NT_STATUS_IS_OK(status)) {
203                 return status;
204         }
205
206         state = gensec_security->private_data;
207         state->initiator = False;
208                 
209         return NT_STATUS_OK;
210 }
211
212 static NTSTATUS schannel_client_start(struct gensec_security *gensec_security) 
213 {
214         NTSTATUS status;
215         struct schannel_state *state;
216
217         status = schannel_start(gensec_security);
218         if (!NT_STATUS_IS_OK(status)) {
219                 return status;
220         }
221
222         state = gensec_security->private_data;
223         state->initiator = True;
224                 
225         return NT_STATUS_OK;
226 }
227
228
229 static BOOL schannel_have_feature(struct gensec_security *gensec_security,
230                                          uint32_t feature)
231 {
232         if (feature & (GENSEC_FEATURE_SIGN | 
233                        GENSEC_FEATURE_SEAL)) {
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 }