(Hopefully) fix the problem Kai reported with
[ira/wip.git] / source3 / libsmb / credentials.c
1 /* 
2    Unix SMB/CIFS implementation.
3    code to manipulate domain credentials
4    Copyright (C) Andrew Tridgell 1997-1998
5    Largely rewritten by Jeremy Allison 2005.
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "../lib/crypto/crypto.h"
23 #include "libcli/auth/libcli_auth.h"
24
25 /****************************************************************************
26  Represent a credential as a string.
27 ****************************************************************************/
28
29 char *credstr(const unsigned char *cred)
30 {
31         char *result;
32         result = talloc_asprintf(talloc_tos(),
33                                  "%02X%02X%02X%02X%02X%02X%02X%02X",
34                                  cred[0], cred[1], cred[2], cred[3],
35                                  cred[4], cred[5], cred[6], cred[7]);
36         SMB_ASSERT(result != NULL);
37         return result;
38 }
39
40 /****************************************************************************
41  Setup the session key and the client and server creds in dc.
42  ADS-style 128 bit session keys.
43  Used by both client and server creds setup.
44 ****************************************************************************/
45
46 static void creds_init_128(struct dcinfo *dc,
47                            const struct netr_Credential *clnt_chal_in,
48                            const struct netr_Credential *srv_chal_in,
49                            const unsigned char mach_pw[16])
50 {
51         unsigned char zero[4], tmp[16];
52         HMACMD5Context ctx;
53         struct MD5Context md5;
54
55         /* Just in case this isn't already there */
56         memcpy(dc->mach_pw, mach_pw, 16);
57
58         ZERO_STRUCT(dc->sess_key);
59
60         memset(zero, 0, sizeof(zero));
61
62         hmac_md5_init_rfc2104(mach_pw, 16, &ctx);
63         MD5Init(&md5);
64         MD5Update(&md5, zero, sizeof(zero));
65         MD5Update(&md5, clnt_chal_in->data, 8);
66         MD5Update(&md5, srv_chal_in->data, 8);
67         MD5Final(tmp, &md5);
68         hmac_md5_update(tmp, sizeof(tmp), &ctx);
69         hmac_md5_final(dc->sess_key, &ctx);
70
71         /* debug output */
72         DEBUG(5,("creds_init_128\n"));
73         DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
74         DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
75         dump_data_pw("\tsession_key ", (const unsigned char *)dc->sess_key, 16);
76
77         /* Generate the next client and server creds. */
78         
79         des_crypt112(dc->clnt_chal.data,                /* output */
80                         clnt_chal_in->data,             /* input */
81                         dc->sess_key,                   /* input */
82                         1);
83
84         des_crypt112(dc->srv_chal.data,                 /* output */
85                         srv_chal_in->data,              /* input */
86                         dc->sess_key,                   /* input */
87                         1);
88
89         /* Seed is the client chal. */
90         memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
91 }
92
93 /****************************************************************************
94  Setup the session key and the client and server creds in dc.
95  Used by both client and server creds setup.
96 ****************************************************************************/
97
98 static void creds_init_64(struct dcinfo *dc,
99                           const struct netr_Credential *clnt_chal_in,
100                           const struct netr_Credential *srv_chal_in,
101                           const unsigned char mach_pw[16])
102 {
103         uint32 sum[2];
104         unsigned char sum2[8];
105
106         /* Just in case this isn't already there */
107         if (dc->mach_pw != mach_pw) {
108                 memcpy(dc->mach_pw, mach_pw, 16);
109         }
110
111         sum[0] = IVAL(clnt_chal_in->data, 0) + IVAL(srv_chal_in->data, 0);
112         sum[1] = IVAL(clnt_chal_in->data, 4) + IVAL(srv_chal_in->data, 4);
113
114         SIVAL(sum2,0,sum[0]);
115         SIVAL(sum2,4,sum[1]);
116
117         ZERO_STRUCT(dc->sess_key);
118
119         des_crypt128(dc->sess_key, sum2, dc->mach_pw);
120
121         /* debug output */
122         DEBUG(5,("creds_init_64\n"));
123         DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
124         DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
125         DEBUG(5,("\tclnt+srv : %s\n", credstr(sum2)));
126         DEBUG(5,("\tsess_key_out : %s\n", credstr(dc->sess_key)));
127
128         /* Generate the next client and server creds. */
129         
130         des_crypt112(dc->clnt_chal.data,                /* output */
131                         clnt_chal_in->data,             /* input */
132                         dc->sess_key,                   /* input */
133                         1);
134
135         des_crypt112(dc->srv_chal.data,                 /* output */
136                         srv_chal_in->data,              /* input */
137                         dc->sess_key,                   /* input */
138                         1);
139
140         /* Seed is the client chal. */
141         memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
142 }
143
144 /****************************************************************************
145  Utility function to step credential chain one forward.
146  Deliberately doesn't update the seed. See reseed comment below.
147 ****************************************************************************/
148
149 static void creds_step(struct dcinfo *dc)
150 {
151         struct netr_Credential time_chal;
152
153         DEBUG(5,("\tsequence = 0x%x\n", (unsigned int)dc->sequence ));
154
155         DEBUG(5,("\tseed:        %s\n", credstr(dc->seed_chal.data) ));
156
157         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence);
158         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
159                                                                                                    
160         DEBUG(5,("\tseed+seq   %s\n", credstr(time_chal.data) ));
161
162         des_crypt112(dc->clnt_chal.data, time_chal.data, dc->sess_key, 1);
163
164         DEBUG(5,("\tCLIENT      %s\n", credstr(dc->clnt_chal.data) ));
165
166         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
167         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
168
169         DEBUG(5,("\tseed+seq+1   %s\n", credstr(time_chal.data) ));
170
171         des_crypt112(dc->srv_chal.data, time_chal.data, dc->sess_key, 1);
172
173         DEBUG(5,("\tSERVER      %s\n", credstr(dc->srv_chal.data) ));
174 }
175
176 /****************************************************************************
177  Create a server credential struct.
178 ****************************************************************************/
179
180 void creds_server_init(uint32 neg_flags,
181                         struct dcinfo *dc,
182                         struct netr_Credential *clnt_chal,
183                         struct netr_Credential *srv_chal,
184                         const unsigned char mach_pw[16],
185                         struct netr_Credential *init_chal_out)
186 {
187         DEBUG(10,("creds_server_init: neg_flags : %x\n", (unsigned int)neg_flags));
188         DEBUG(10,("creds_server_init: client chal : %s\n", credstr(clnt_chal->data) ));
189         DEBUG(10,("creds_server_init: server chal : %s\n", credstr(srv_chal->data) ));
190         dump_data_pw("creds_server_init: machine pass", mach_pw, 16);
191
192         /* Generate the session key and the next client and server creds. */
193         if (neg_flags & NETLOGON_NEG_128BIT) {
194                 creds_init_128(dc,
195                         clnt_chal,
196                         srv_chal,
197                         mach_pw);
198         } else {
199                 creds_init_64(dc,
200                         clnt_chal,
201                         srv_chal,
202                         mach_pw);
203         }
204
205         dump_data_pw("creds_server_init: session key", dc->sess_key, 16);
206
207         DEBUG(10,("creds_server_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
208         DEBUG(10,("creds_server_init: server : %s\n", credstr(dc->srv_chal.data) ));
209         DEBUG(10,("creds_server_init: seed : %s\n", credstr(dc->seed_chal.data) ));
210
211         memcpy(init_chal_out->data, dc->srv_chal.data, 8);
212 }
213
214 /****************************************************************************
215  Check a credential sent by the client.
216 ****************************************************************************/
217
218 bool netlogon_creds_server_check(const struct dcinfo *dc,
219                                  const struct netr_Credential *rcv_cli_chal_in)
220 {
221         if (memcmp(dc->clnt_chal.data, rcv_cli_chal_in->data, 8)) {
222                 DEBUG(5,("netlogon_creds_server_check: challenge : %s\n",
223                         credstr(rcv_cli_chal_in->data)));
224                 DEBUG(5,("calculated: %s\n", credstr(dc->clnt_chal.data)));
225                 DEBUG(2,("netlogon_creds_server_check: credentials check failed.\n"));
226                 return false;
227         }
228
229         DEBUG(10,("netlogon_creds_server_check: credentials check OK.\n"));
230
231         return true;
232 }
233 /****************************************************************************
234  Replace current seed chal. Internal function - due to split server step below.
235 ****************************************************************************/
236
237 static void creds_reseed(struct dcinfo *dc)
238 {
239         struct netr_Credential time_chal;
240
241         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
242         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
243
244         dc->seed_chal = time_chal;
245
246         DEBUG(5,("cred_reseed: seed %s\n", credstr(dc->seed_chal.data) ));
247 }
248
249 /****************************************************************************
250  Step the server credential chain one forward. 
251 ****************************************************************************/
252
253 bool netlogon_creds_server_step(struct dcinfo *dc,
254                                 const struct netr_Authenticator *received_cred,
255                                 struct netr_Authenticator *cred_out)
256 {
257         bool ret;
258         struct dcinfo tmp_dc = *dc;
259
260         if (!received_cred || !cred_out) {
261                 return false;
262         }
263
264         /* Do all operations on a temporary copy of the dc,
265            which we throw away if the checks fail. */
266
267         tmp_dc.sequence = received_cred->timestamp;
268
269         creds_step(&tmp_dc);
270
271         /* Create the outgoing credentials */
272         cred_out->timestamp = tmp_dc.sequence + 1;
273         memcpy(&cred_out->cred, &tmp_dc.srv_chal, sizeof(cred_out->cred));
274
275         creds_reseed(&tmp_dc);
276
277         ret = netlogon_creds_server_check(&tmp_dc, &received_cred->cred);
278         if (!ret) {
279                 return false;
280         }
281
282         /* creds step succeeded - replace the current creds. */
283         *dc = tmp_dc;
284         return true;
285 }
286
287 void cred_hash3(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw)
288 {
289         unsigned char key2[8];
290
291         memset(key2,'\0',8);
292         des_crypt56(out, in, key, forw);
293         key2[0] = key[7];
294         des_crypt56(out + 8, in + 8, key2, forw);
295 }