split up the schannel rpc client code into separate key establishment
[samba.git] / source / librpc / rpc / dcerpc_schannel.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    dcerpc schannel operations
5
6    Copyright (C) Andrew Tridgell 2004
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 /*
26   wrappers for the schannel_*() functions
27 */
28 static NTSTATUS schan_unseal_packet(struct dcerpc_security *dcerpc_security, 
29                                   uchar *data, size_t length, DATA_BLOB *sig)
30 {
31         struct schannel_state *schannel_state = dcerpc_security->private;
32         return schannel_unseal_packet(schannel_state, data, length, sig);
33 }
34
35 static NTSTATUS schan_check_packet(struct dcerpc_security *dcerpc_security, 
36                                   const uchar *data, size_t length, 
37                                   const DATA_BLOB *sig)
38 {
39         struct schannel_state *schannel_state = dcerpc_security->private;
40         return schannel_check_packet(schannel_state, data, length, sig);
41 }
42
43 static NTSTATUS schan_seal_packet(struct dcerpc_security *dcerpc_security, 
44                                  uchar *data, size_t length, 
45                                  DATA_BLOB *sig)
46 {
47         struct schannel_state *schannel_state = dcerpc_security->private;
48         return schannel_seal_packet(schannel_state, data, length, sig);
49 }
50
51 static NTSTATUS schan_sign_packet(struct dcerpc_security *dcerpc_security, 
52                                  const uchar *data, size_t length, 
53                                  DATA_BLOB *sig)
54 {
55         struct schannel_state *schannel_state = dcerpc_security->private;
56         return schannel_sign_packet(schannel_state, data, length, sig);
57 }
58
59 static void schan_security_end(struct dcerpc_security *dcerpc_security)
60 {
61         struct schannel_state *schannel_state = dcerpc_security->private;
62         schannel_end(&schannel_state);
63 }
64
65
66 /*
67   get a schannel key using a netlogon challenge on a secondary pipe
68 */
69 NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
70                              const char *domain,
71                              const char *username,
72                              const char *password,
73                              int chan_type,
74                              uint8 new_session_key[8])
75 {
76         NTSTATUS status;
77         struct dcerpc_pipe *p2;
78         struct netr_ServerReqChallenge r;
79         struct netr_ServerAuthenticate2 a;
80         uint8 mach_pwd[16];
81         struct netr_CredentialState creds;
82         const char *workgroup, *workstation;
83         uint32 negotiate_flags = 0;
84
85         workstation = username;
86         workgroup = domain;
87
88         /*
89           step 1 - establish a netlogon connection, with no authentication
90         */
91         status = dcerpc_secondary_smb(p, &p2, 
92                                       DCERPC_NETLOGON_NAME, 
93                                       DCERPC_NETLOGON_UUID, 
94                                       DCERPC_NETLOGON_VERSION);
95
96
97         /*
98           step 2 - request a netlogon challenge
99         */
100         r.in.server_name = talloc_asprintf(p->mem_ctx, "\\\\%s", dcerpc_server_name(p));
101         r.in.computer_name = workstation;
102         generate_random_buffer(r.in.credentials.data, sizeof(r.in.credentials.data), False);
103
104         status = dcerpc_netr_ServerReqChallenge(p2, p->mem_ctx, &r);
105         if (!NT_STATUS_IS_OK(status)) {
106                 return status;
107         }
108
109         /*
110           step 3 - authenticate on the netlogon pipe
111         */
112         E_md4hash(password, mach_pwd);
113         creds_client_init(&creds, &r.in.credentials, &r.out.credentials, mach_pwd,
114                           &a.in.credentials);
115
116         a.in.server_name = r.in.server_name;
117         a.in.username = talloc_asprintf(p->mem_ctx, "%s$", workstation);
118         a.in.secure_channel_type = chan_type;
119         a.in.computer_name = workstation;
120         a.in.negotiate_flags = &negotiate_flags;
121         a.out.negotiate_flags = &negotiate_flags;
122
123         status = dcerpc_netr_ServerAuthenticate2(p2, p->mem_ctx, &a);
124         if (!NT_STATUS_IS_OK(status)) {
125                 return status;
126         }
127         if (!creds_client_check(&creds, &a.out.credentials)) {
128                 return NT_STATUS_UNSUCCESSFUL;
129         }
130
131         /*
132           the schannel session key is now in creds.session_key
133
134           we no longer need the netlogon pipe open
135         */
136         dcerpc_pipe_close(p2);
137
138         memcpy(new_session_key, creds.session_key, 8);
139
140         return NT_STATUS_OK;
141 }
142
143
144 /*
145   do a schannel style bind on a dcerpc pipe with the given schannel
146   key. The username is usually of the form HOSTNAME$ and the password
147   is the domain trust password
148 */
149 NTSTATUS dcerpc_bind_auth_schannel_key(struct dcerpc_pipe *p,
150                                        const char *uuid, unsigned version,
151                                        const char *domain,
152                                        const char *username,
153                                        const uint8 session_key[8])
154 {
155         NTSTATUS status;
156         uint8 full_session_key[16];
157         struct schannel_state *schannel_state;
158         const char *workgroup, *workstation;
159
160         memcpy(full_session_key, session_key, 8);
161         memset(full_session_key+8, 0, 8);
162
163         workstation = username;
164         workgroup = domain;
165
166         /*
167           perform a bind with security type schannel
168         */
169         p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info));
170         if (!p->auth_info) {
171                 status = NT_STATUS_NO_MEMORY;
172                 goto done;
173         }
174
175         p->auth_info->auth_type = DCERPC_AUTH_TYPE_SCHANNEL;
176         
177         if (p->flags & DCERPC_SEAL) {
178                 p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
179         } else {
180                 /* note that DCERPC_AUTH_LEVEL_NONE does not make any 
181                    sense, and would be rejected by the server */
182                 p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
183         }
184         p->auth_info->auth_pad_length = 0;
185         p->auth_info->auth_reserved = 0;
186         p->auth_info->auth_context_id = random();
187         p->security_state = NULL;
188
189         p->auth_info->credentials = data_blob_talloc(p->mem_ctx, 
190                                                      NULL,
191                                                      8 +
192                                                      strlen(workgroup)+1 +
193                                                      strlen(workstation)+1);
194         if (!p->auth_info->credentials.data) {
195                 return NT_STATUS_NO_MEMORY;
196         }
197
198         /* oh, this is ugly! */
199         SIVAL(p->auth_info->credentials.data, 0, 0);
200         SIVAL(p->auth_info->credentials.data, 4, 3);
201         memcpy(p->auth_info->credentials.data+8, workgroup, strlen(workgroup)+1);
202         memcpy(p->auth_info->credentials.data+8+strlen(workgroup)+1, 
203                workstation, strlen(workstation)+1);
204
205         /* send the authenticated bind request */
206         status = dcerpc_bind_byuuid(p, p->mem_ctx, uuid, version);
207         if (!NT_STATUS_IS_OK(status)) {
208                 goto done;
209         }
210
211         p->security_state = talloc_p(p->mem_ctx, struct dcerpc_security);
212         if (!p->security_state) {
213                 status = NT_STATUS_NO_MEMORY;
214                 goto done;
215         }
216
217         schannel_state = talloc_p(p->mem_ctx, struct schannel_state);
218         if (!schannel_state) {
219                 status = NT_STATUS_NO_MEMORY;
220                 goto done;
221         }
222
223         status = schannel_start(&schannel_state, full_session_key, True);
224         if (!NT_STATUS_IS_OK(status)) {
225                 goto done;
226         }
227
228         dump_data_pw("session key:\n", schannel_state->session_key, 16);
229
230         p->security_state->private = schannel_state;
231         p->security_state->unseal_packet = schan_unseal_packet;
232         p->security_state->check_packet = schan_check_packet;
233         p->security_state->seal_packet = schan_seal_packet;
234         p->security_state->sign_packet = schan_sign_packet;
235         p->security_state->security_end = schan_security_end;
236
237 done:
238         return status;
239 }
240
241
242 /*
243   do a schannel style bind on a dcerpc pipe. The username is usually
244   of the form HOSTNAME$ and the password is the domain trust password
245 */
246 NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p,
247                                    const char *uuid, unsigned version,
248                                    const char *domain,
249                                    const char *username,
250                                    const char *password)
251 {
252         NTSTATUS status;
253         uint8 session_key[8];
254
255         status = dcerpc_schannel_key(p, domain, username, password, 
256                                      lp_server_role() == ROLE_DOMAIN_BDC? SEC_CHAN_BDC:SEC_CHAN_WKSTA,
257                                      session_key);
258         if (!NT_STATUS_IS_OK(status)) {
259                 return status;
260         }
261
262         status = dcerpc_bind_auth_schannel_key(p, uuid, version, domain, username, session_key);
263
264         return status;
265 }
266