don't always use the same schannel context number
[samba.git] / source4 / 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         return schannel_end(&schannel_state);
63 }
64
65
66 /*
67   do a schannel style bind on a dcerpc pipe. The username is usually
68   of the form HOSTNAME$ and the password is the domain trust password
69 */
70 NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p,
71                                    const char *uuid, unsigned version,
72                                    const char *domain,
73                                    const char *username,
74                                    const char *password)
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         uint8 session_key[16];
82         struct netr_CredentialState creds;
83         struct schannel_state *schannel_state;
84         const char *workgroup, *workstation;
85         uint32 negotiate_flags = 0;
86
87         workstation = username;
88         workgroup = domain;
89
90         /*
91           step 1 - establish a netlogon connection, with no authentication
92         */
93         status = dcerpc_secondary_smb(p, &p2, 
94                                       DCERPC_NETLOGON_NAME, 
95                                       DCERPC_NETLOGON_UUID, 
96                                       DCERPC_NETLOGON_VERSION);
97
98
99         /*
100           step 2 - request a netlogon challenge
101         */
102         r.in.server_name = talloc_asprintf(p->mem_ctx, "\\\\%s", dcerpc_server_name(p));
103         r.in.computer_name = workstation;
104         generate_random_buffer(r.in.credentials.data, sizeof(r.in.credentials.data), False);
105
106         status = dcerpc_netr_ServerReqChallenge(p2, p->mem_ctx, &r);
107         if (!NT_STATUS_IS_OK(status)) {
108                 return status;
109         }
110
111         /*
112           step 3 - authenticate on the netlogon pipe
113         */
114         E_md4hash(password, mach_pwd);
115         creds_client_init(&creds, &r.in.credentials, &r.out.credentials, mach_pwd,
116                           &a.in.credentials);
117
118         a.in.server_name = r.in.server_name;
119         a.in.username = talloc_asprintf(p->mem_ctx, "%s$", workstation);
120         if (lp_server_role() == ROLE_DOMAIN_BDC) {
121                 a.in.secure_channel_type = SEC_CHAN_BDC;
122         } else {
123                 a.in.secure_channel_type = SEC_CHAN_WKSTA;
124         }
125         a.in.computer_name = workstation;
126         a.in.negotiate_flags = &negotiate_flags;
127         a.out.negotiate_flags = &negotiate_flags;
128
129         status = dcerpc_netr_ServerAuthenticate2(p2, p->mem_ctx, &a);
130         if (!NT_STATUS_IS_OK(status)) {
131                 return status;
132         }
133         if (!creds_client_check(&creds, &a.out.credentials)) {
134                 return NT_STATUS_UNSUCCESSFUL;
135         }
136
137         /*
138           the schannel session key is now in creds.session_key
139         */
140
141
142         /*
143           step 4 - perform a bind with security type schannel
144         */
145         p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info));
146         if (!p->auth_info) {
147                 status = NT_STATUS_NO_MEMORY;
148                 goto done;
149         }
150
151         p->auth_info->auth_type = DCERPC_AUTH_TYPE_SCHANNEL;
152         
153         if (p->flags & DCERPC_SEAL) {
154                 p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
155         } else {
156                 /* note that DCERPC_AUTH_LEVEL_NONE does not make any 
157                    sense, and would be rejected by the server */
158                 p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
159         }
160         p->auth_info->auth_pad_length = 0;
161         p->auth_info->auth_reserved = 0;
162         p->auth_info->auth_context_id = random();
163         p->security_state = NULL;
164
165         p->auth_info->credentials = data_blob_talloc(p->mem_ctx, 
166                                                      NULL,
167                                                      8 +
168                                                      strlen(workgroup)+1 +
169                                                      strlen(workstation)+1);
170         if (!p->auth_info->credentials.data) {
171                 return NT_STATUS_NO_MEMORY;
172         }
173
174         /* oh, this is ugly! */
175         SIVAL(p->auth_info->credentials.data, 0, 0);
176         SIVAL(p->auth_info->credentials.data, 4, 3);
177         memcpy(p->auth_info->credentials.data+8, workgroup, strlen(workgroup)+1);
178         memcpy(p->auth_info->credentials.data+8+strlen(workgroup)+1, 
179                workstation, strlen(workstation)+1);
180
181         /* send the authenticated bind request */
182         status = dcerpc_bind_byuuid(p, p->mem_ctx, uuid, version);
183         if (!NT_STATUS_IS_OK(status)) {
184                 goto done;
185         }
186
187         p->security_state = talloc_p(p->mem_ctx, struct dcerpc_security);
188         if (!p->security_state) {
189                 status = NT_STATUS_NO_MEMORY;
190                 goto done;
191         }
192
193         schannel_state = talloc_p(p->mem_ctx, struct schannel_state);
194         if (!schannel_state) {
195                 status = NT_STATUS_NO_MEMORY;
196                 goto done;
197         }
198
199         memcpy(session_key, creds.session_key, 8);
200         memset(session_key+8, 0, 8);
201
202         status = schannel_start(&schannel_state, session_key, True);
203         if (!NT_STATUS_IS_OK(status)) {
204                 goto done;
205         }
206
207         dump_data_pw("session key:\n", schannel_state->session_key, 16);
208
209         p->security_state->private = schannel_state;
210         p->security_state->unseal_packet = schan_unseal_packet;
211         p->security_state->check_packet = schan_check_packet;
212         p->security_state->seal_packet = schan_seal_packet;
213         p->security_state->sign_packet = schan_sign_packet;
214         p->security_state->security_end = schan_security_end;
215
216 done:
217         return status;
218 }
219