we can close the netlogon pipe used to setup the schannel session key
[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           we no longer need the netlogon pipe open
141         */
142         dcerpc_pipe_close(p2);
143
144         /*
145           step 4 - perform a bind with security type schannel
146         */
147         p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info));
148         if (!p->auth_info) {
149                 status = NT_STATUS_NO_MEMORY;
150                 goto done;
151         }
152
153         p->auth_info->auth_type = DCERPC_AUTH_TYPE_SCHANNEL;
154         
155         if (p->flags & DCERPC_SEAL) {
156                 p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
157         } else {
158                 /* note that DCERPC_AUTH_LEVEL_NONE does not make any 
159                    sense, and would be rejected by the server */
160                 p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
161         }
162         p->auth_info->auth_pad_length = 0;
163         p->auth_info->auth_reserved = 0;
164         p->auth_info->auth_context_id = random();
165         p->security_state = NULL;
166
167         p->auth_info->credentials = data_blob_talloc(p->mem_ctx, 
168                                                      NULL,
169                                                      8 +
170                                                      strlen(workgroup)+1 +
171                                                      strlen(workstation)+1);
172         if (!p->auth_info->credentials.data) {
173                 return NT_STATUS_NO_MEMORY;
174         }
175
176         /* oh, this is ugly! */
177         SIVAL(p->auth_info->credentials.data, 0, 0);
178         SIVAL(p->auth_info->credentials.data, 4, 3);
179         memcpy(p->auth_info->credentials.data+8, workgroup, strlen(workgroup)+1);
180         memcpy(p->auth_info->credentials.data+8+strlen(workgroup)+1, 
181                workstation, strlen(workstation)+1);
182
183         /* send the authenticated bind request */
184         status = dcerpc_bind_byuuid(p, p->mem_ctx, uuid, version);
185         if (!NT_STATUS_IS_OK(status)) {
186                 goto done;
187         }
188
189         p->security_state = talloc_p(p->mem_ctx, struct dcerpc_security);
190         if (!p->security_state) {
191                 status = NT_STATUS_NO_MEMORY;
192                 goto done;
193         }
194
195         schannel_state = talloc_p(p->mem_ctx, struct schannel_state);
196         if (!schannel_state) {
197                 status = NT_STATUS_NO_MEMORY;
198                 goto done;
199         }
200
201         memcpy(session_key, creds.session_key, 8);
202         memset(session_key+8, 0, 8);
203
204         status = schannel_start(&schannel_state, session_key, True);
205         if (!NT_STATUS_IS_OK(status)) {
206                 goto done;
207         }
208
209         dump_data_pw("session key:\n", schannel_state->session_key, 16);
210
211         p->security_state->private = schannel_state;
212         p->security_state->unseal_packet = schan_unseal_packet;
213         p->security_state->check_packet = schan_check_packet;
214         p->security_state->seal_packet = schan_seal_packet;
215         p->security_state->sign_packet = schan_sign_packet;
216         p->security_state->security_end = schan_security_end;
217
218 done:
219         return status;
220 }
221